[ovs-dev] [PATCH 2/2] Support accepting and displaying port names in OVS tools.

Ben Pfaff blp at ovn.org
Thu May 18 20:28:01 UTC 2017


Until now, most ovs-ofctl commands have not accepted names for ports, only
numbers, and have not been able to display port names either.  It's a lot
easier for users if they can use and see meaningful names instead of
arbitrary numbers.  This commit adds that support.

For backward compatibility, only interactive ovs-ofctl commands by default
display port names; to display them in scripts, use the new --names
option.

Signed-off-by: Ben Pfaff <blp at ovn.org>
---
 NEWS                              |   3 +
 include/openvswitch/match.h       |  15 +-
 include/openvswitch/meta-flow.h   |   6 +-
 include/openvswitch/ofp-actions.h |   9 +-
 include/openvswitch/ofp-parse.h   |  18 +-
 include/openvswitch/ofp-print.h   |  16 +-
 include/openvswitch/ofp-util.h    |  30 +-
 lib/bundle.c                      |  27 +-
 lib/bundle.h                      |  12 +-
 lib/classifier.c                  |   6 +-
 lib/classifier.h                  |   4 +-
 lib/dpctl.c                       |  17 +-
 lib/flow.c                        |  15 +-
 lib/flow.h                        |   8 +-
 lib/learn.c                       |  22 +-
 lib/learn.h                       |  10 +-
 lib/learning-switch.c             |   4 +-
 lib/match.c                       |  43 ++-
 lib/meta-flow.c                   |  25 +-
 lib/nx-match.c                    |   2 +-
 lib/ofp-actions.c                 | 599 ++++++++++++++++++++++++++------------
 lib/ofp-parse.c                   | 127 ++++----
 lib/ofp-print.c                   | 217 ++++++++------
 lib/ofp-util.c                    | 243 ++++++++++++++--
 lib/vconn.c                       |  10 +-
 ofproto/bond.c                    |   6 +-
 ofproto/ofproto-dpif-trace.c      |  18 +-
 ofproto/ofproto-dpif-xlate.c      |  20 +-
 ofproto/ofproto-dpif.c            |   6 +-
 ofproto/ofproto.c                 |   4 +-
 ofproto/tunnel.c                  |  12 +-
 ovn/controller/ofctrl.c           |  16 +-
 ovn/controller/pinctrl.c          |   2 +-
 ovn/lib/expr.c                    |   2 +-
 ovn/utilities/ovn-sbctl.c         |   6 +-
 ovn/utilities/ovn-trace.c         |   6 +-
 tests/ofproto-dpif.at             |   2 +-
 tests/ofproto.at                  |  56 ++--
 tests/ovs-ofctl.at                |  56 +++-
 tests/test-bundle.c               |   4 +-
 tests/test-flows.c                |   8 +-
 tests/test-ovn.c                  |   2 +-
 utilities/ovs-ofctl.8.in          |  32 +-
 utilities/ovs-ofctl.c             | 244 ++++++++++++----
 utilities/ovs-testcontroller.c    |   6 +-
 45 files changed, 1380 insertions(+), 616 deletions(-)

diff --git a/NEWS b/NEWS
index 7a2b185bbd84..9793584f782a 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,8 @@
 Post-v2.7.0
 ---------------------
+   - ovs-ofctl can now accept and display port names in place of numbers.  By
+     default it always accepts names and in interactive use it displays them;
+     use --names or --no-names to override.  See ovs-ofctl(8) for details.
    - Tunnels:
      * Added support to set packet mark for tunnel endpoint using
        `egress_pkt_mark` OVSDB option.
diff --git a/include/openvswitch/match.h b/include/openvswitch/match.h
index 06fa04c43709..ca1dc7df9ce2 100644
--- a/include/openvswitch/match.h
+++ b/include/openvswitch/match.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Nicira, Inc.
+ * Copyright (c) 2009-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.
@@ -22,6 +22,7 @@
 #include "openvswitch/tun-metadata.h"
 
 struct ds;
+struct ofputil_port_map;
 
 /* A flow classification match.
  *
@@ -186,9 +187,11 @@ uint32_t match_hash(const struct match *, uint32_t basis);
 void match_init_hidden_fields(struct match *);
 bool match_has_default_hidden_fields(const struct match *);
 
-void match_format(const struct match *, struct ds *, int priority);
-char *match_to_string(const struct match *, int priority);
-void match_print(const struct match *);
+void match_format(const struct match *, const struct ofputil_port_map *,
+                  struct ds *, int priority);
+char *match_to_string(const struct match *, const struct ofputil_port_map *,
+                      int priority);
+void match_print(const struct match *, const struct ofputil_port_map *);
 
 /* Compressed match. */
 
@@ -228,7 +231,9 @@ bool minimatch_equal(const struct minimatch *a, const struct minimatch *b);
 bool minimatch_matches_flow(const struct minimatch *, const struct flow *);
 
 void minimatch_format(const struct minimatch *, const struct tun_table *,
+                      const struct ofputil_port_map *,
                       struct ds *, int priority);
-char *minimatch_to_string(const struct minimatch *, int priority);
+char *minimatch_to_string(const struct minimatch *,
+                          const struct ofputil_port_map *, int priority);
 
 #endif /* match.h */
diff --git a/include/openvswitch/meta-flow.h b/include/openvswitch/meta-flow.h
index 11852d2a07d8..d8e4067d61f9 100644
--- a/include/openvswitch/meta-flow.h
+++ b/include/openvswitch/meta-flow.h
@@ -30,6 +30,7 @@
 
 struct ds;
 struct match;
+struct ofputil_port_map;
 struct ofputil_tlv_table_mod;
 
 /* Open vSwitch fields
@@ -2100,10 +2101,13 @@ enum ofperr mf_check_dst(const struct mf_subfield *, const struct match *);
 
 /* Parsing and formatting. */
 char *mf_parse(const struct mf_field *, const char *,
+               const struct ofputil_port_map *,
                union mf_value *value, union mf_value *mask);
-char *mf_parse_value(const struct mf_field *, const char *, union mf_value *);
+char *mf_parse_value(const struct mf_field *, const char *,
+                     const struct ofputil_port_map *, union mf_value *);
 void mf_format(const struct mf_field *,
                const union mf_value *value, const union mf_value *mask,
+               const struct ofputil_port_map *,
                struct ds *);
 void mf_format_subvalue(const union mf_subvalue *subvalue, struct ds *s);
 
diff --git a/include/openvswitch/ofp-actions.h b/include/openvswitch/ofp-actions.h
index 324aac89289d..7b4aa9201d9b 100644
--- a/include/openvswitch/ofp-actions.h
+++ b/include/openvswitch/ofp-actions.h
@@ -1017,11 +1017,14 @@ const struct mf_field *ofpact_get_mf_dst(const struct ofpact *ofpact);
 uint32_t ofpacts_get_meter(const struct ofpact[], size_t ofpacts_len);
 
 /* Formatting and parsing ofpacts. */
-void ofpacts_format(const struct ofpact[], size_t ofpacts_len, struct ds *);
-char *ofpacts_parse_actions(const char *, struct ofpbuf *ofpacts,
+void ofpacts_format(const struct ofpact[], size_t ofpacts_len,
+                    const struct ofputil_port_map *, struct ds *);
+char *ofpacts_parse_actions(const char *, const struct ofputil_port_map *,
+                            struct ofpbuf *ofpacts,
                             enum ofputil_protocol *usable_protocols)
     OVS_WARN_UNUSED_RESULT;
-char *ofpacts_parse_instructions(const char *, struct ofpbuf *ofpacts,
+char *ofpacts_parse_instructions(const char *, const struct ofputil_port_map *,
+                                 struct ofpbuf *ofpacts,
                                  enum ofputil_protocol *usable_protocols)
     OVS_WARN_UNUSED_RESULT;
 const char *ofpact_name(enum ofpact_type);
diff --git a/include/openvswitch/ofp-parse.h b/include/openvswitch/ofp-parse.h
index ed7a09c908b1..fc5784e11e36 100644
--- a/include/openvswitch/ofp-parse.h
+++ b/include/openvswitch/ofp-parse.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2011, 2012, 2013, 2014, 2015, 2016 Nicira, Inc.
+ * Copyright (c) 2010, 2011, 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.
@@ -40,15 +40,17 @@ struct simap;
 enum ofputil_protocol;
 
 char *parse_ofp_str(struct ofputil_flow_mod *, int command, const char *str_,
+                    const struct ofputil_port_map *,
                     enum ofputil_protocol *usable_protocols)
     OVS_WARN_UNUSED_RESULT;
 
 char *parse_ofp_flow_mod_str(struct ofputil_flow_mod *, const char *string,
-                             int command,
+                             const struct ofputil_port_map *, int command,
                              enum ofputil_protocol *usable_protocols)
     OVS_WARN_UNUSED_RESULT;
 
 char *parse_ofp_packet_out_str(struct ofputil_packet_out *po, const char *str_,
+                               const struct ofputil_port_map *,
                                enum ofputil_protocol *usable_protocols)
     OVS_WARN_UNUSED_RESULT;
 
@@ -57,19 +59,21 @@ char *parse_ofp_table_mod(struct ofputil_table_mod *,
                           uint32_t *usable_versions)
     OVS_WARN_UNUSED_RESULT;
 
-char *parse_ofp_flow_mod_file(const char *file_name, int command,
+char *parse_ofp_flow_mod_file(const char *file_name,
+                              const struct ofputil_port_map *, int command,
                               struct ofputil_flow_mod **fms, size_t *n_fms,
                               enum ofputil_protocol *usable_protocols)
     OVS_WARN_UNUSED_RESULT;
 
 char *parse_ofp_flow_stats_request_str(struct ofputil_flow_stats_request *,
                                        bool aggregate, const char *string,
+                                       const struct ofputil_port_map *,
                                        enum ofputil_protocol *usable_protocols)
     OVS_WARN_UNUSED_RESULT;
 
 char *parse_ofp_exact_flow(struct flow *flow, struct flow_wildcards *wc,
                            const struct tun_table *tun_table, const char *s,
-                           const struct simap *portno_names);
+                           const struct ofputil_port_map *port_map);
 
 char *parse_ofp_meter_mod_str(struct ofputil_meter_mod *, const char *string,
                               int command,
@@ -78,20 +82,24 @@ char *parse_ofp_meter_mod_str(struct ofputil_meter_mod *, const char *string,
 
 char *parse_flow_monitor_request(struct ofputil_flow_monitor_request *,
                                  const char *,
+                                 const struct ofputil_port_map *port_map,
                                  enum ofputil_protocol *usable_protocols)
     OVS_WARN_UNUSED_RESULT;
 
-char *parse_ofp_group_mod_file(const char *file_name, int command,
+char *parse_ofp_group_mod_file(const char *file_name,
+                               const struct ofputil_port_map *, int command,
                                struct ofputil_group_mod **gms, size_t *n_gms,
                                enum ofputil_protocol *usable_protocols)
     OVS_WARN_UNUSED_RESULT;
 
 char *parse_ofp_group_mod_str(struct ofputil_group_mod *, int command,
                               const char *string,
+                              const struct ofputil_port_map *,
                               enum ofputil_protocol *usable_protocols)
     OVS_WARN_UNUSED_RESULT;
 
 char *parse_ofp_bundle_file(const char *file_name,
+                            const struct ofputil_port_map *,
                             struct ofputil_bundle_msg **, size_t *n_bms,
                             enum ofputil_protocol *)
     OVS_WARN_UNUSED_RESULT;
diff --git a/include/openvswitch/ofp-print.h b/include/openvswitch/ofp-print.h
index 08b642900434..4893d44b4b48 100644
--- a/include/openvswitch/ofp-print.h
+++ b/include/openvswitch/ofp-print.h
@@ -37,25 +37,31 @@ struct dp_packet;
 extern "C" {
 #endif
 
-void ofp_print(FILE *, const void *, size_t, int verbosity);
+void ofp_print(FILE *, const void *, size_t, const struct ofputil_port_map *,
+               int verbosity);
 void ofp_print_packet(FILE *stream, const void *data,
                       size_t len, ovs_be32 packet_type);
 void ofp_print_dp_packet(FILE *stream, const struct dp_packet *packet);
 
-void ofp10_match_print(struct ds *, const struct ofp10_match *, int verbosity);
+void ofp10_match_print(struct ds *, const struct ofp10_match *,
+                       const struct ofputil_port_map *, int verbosity);
 
-char *ofp_to_string(const void *, size_t, int verbosity);
-char *ofp10_match_to_string(const struct ofp10_match *, int verbosity);
+char *ofp_to_string(const void *, size_t, const struct ofputil_port_map *,
+                    int verbosity);
+char *ofp10_match_to_string(const struct ofp10_match *,
+                            const struct ofputil_port_map *, int verbosity);
 char *ofp_packet_to_string(const void *data, size_t len, ovs_be32 packet_type);
 char *ofp_dp_packet_to_string(const struct dp_packet *packet);
 
-void ofp_print_flow_stats(struct ds *, const struct ofputil_flow_stats *);
 void ofp_print_version(const struct ofp_header *, struct ds *);
 void ofp_print_table_features(
     struct ds *, const struct ofputil_table_features *features,
     const struct ofputil_table_features *prev_features,
     const struct ofputil_table_stats *stats,
     const struct ofputil_table_stats *prev_stats);
+
+void ofp_print_flow_stats(struct ds *, const struct ofputil_flow_stats *,
+                          const struct ofputil_port_map *);
 
 #ifdef  __cplusplus
 }
diff --git a/include/openvswitch/ofp-util.h b/include/openvswitch/ofp-util.h
index f37d181599b2..3294349ddb39 100644
--- a/include/openvswitch/ofp-util.h
+++ b/include/openvswitch/ofp-util.h
@@ -21,6 +21,7 @@
 #include <stddef.h>
 #include <stdint.h>
 #include "openvswitch/flow.h"
+#include "openvswitch/hmap.h"
 #include "openvswitch/list.h"
 #include "openvswitch/match.h"
 #include "openvswitch/meta-flow.h"
@@ -38,15 +39,36 @@ union ofp_action;
 struct ofpact_set_field;
 struct vl_mff_map;
 
+/* Mapping between port numbers and names. */
+struct ofputil_port_map {
+    struct hmap by_name;
+    struct hmap by_number;
+};
+
+#define OFPUTIL_PORT_MAP_INITIALIZER(MAP)  \
+    { HMAP_INITIALIZER(&(MAP)->by_name), HMAP_INITIALIZER(&(MAP)->by_number) }
+
+void ofputil_port_map_init(struct ofputil_port_map *);
+const char *ofputil_port_map_get_name(const struct ofputil_port_map *,
+                                      ofp_port_t);
+ofp_port_t ofputil_port_map_get_number(const struct ofputil_port_map *,
+                                      const char *name);
+void ofputil_port_map_put(struct ofputil_port_map *,
+                          ofp_port_t, const char *name);
+void ofputil_port_map_destroy(struct ofputil_port_map *);
+
 /* Port numbers. */
 enum ofperr ofputil_port_from_ofp11(ovs_be32 ofp11_port,
                                     ofp_port_t *ofp10_port);
 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[OFP10_MAX_PORT_NAME_LEN],
-                            size_t bufsize);
+bool ofputil_port_from_string(const char *, const struct ofputil_port_map *,
+                              ofp_port_t *portp);
+const char *ofputil_port_get_reserved_name(ofp_port_t);
+void ofputil_format_port(ofp_port_t port, const struct ofputil_port_map *,
+                         struct ds *);
+void ofputil_port_to_string(ofp_port_t, const struct ofputil_port_map *,
+                            char *namebuf, size_t bufsize);
 
 /* Group numbers. */
 enum { MAX_GROUP_NAME_LEN = INT_STRLEN(uint32_t) };
diff --git a/lib/bundle.c b/lib/bundle.c
index 1bacb3779448..e6705a085999 100644
--- a/lib/bundle.c
+++ b/lib/bundle.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016 Nicira, Inc.
+/* Copyright (c) 2011, 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.
@@ -145,7 +145,8 @@ bundle_check(const struct ofpact_bundle *bundle, ofp_port_t max_ports,
  * Returns NULL if successful, otherwise a malloc()'d string describing the
  * error.  The caller is responsible for freeing the returned string.*/
 static char * OVS_WARN_UNUSED_RESULT
-bundle_parse__(const char *s, char **save_ptr,
+bundle_parse__(const char *s, const struct ofputil_port_map *port_map,
+               char **save_ptr,
                const char *fields, const char *basis, const char *algorithm,
                const char *slave_type, const char *dst,
                const char *slave_delim, struct ofpbuf *ofpacts)
@@ -172,7 +173,7 @@ bundle_parse__(const char *s, char **save_ptr,
             break;
         }
 
-        if (!ofputil_port_from_string(slave, &slave_port)) {
+        if (!ofputil_port_from_string(slave, port_map, &slave_port)) {
             return xasprintf("%s: bad port number", slave);
         }
         ofpbuf_put(ofpacts, &slave_port, sizeof slave_port);
@@ -232,7 +233,8 @@ bundle_parse__(const char *s, char **save_ptr,
  * 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
-bundle_parse(const char *s, struct ofpbuf *ofpacts)
+bundle_parse(const char *s, const struct ofputil_port_map *port_map,
+             struct ofpbuf *ofpacts)
 {
     char *fields, *basis, *algorithm, *slave_type, *slave_delim;
     char *tokstr, *save_ptr;
@@ -246,7 +248,8 @@ bundle_parse(const char *s, struct ofpbuf *ofpacts)
     slave_type = strtok_r(NULL, ", ", &save_ptr);
     slave_delim = strtok_r(NULL, ": ", &save_ptr);
 
-    error = bundle_parse__(s, &save_ptr, fields, basis, algorithm, slave_type,
+    error = bundle_parse__(s, port_map,
+                           &save_ptr, fields, basis, algorithm, slave_type,
                            NULL, slave_delim, ofpacts);
     free(tokstr);
 
@@ -259,7 +262,8 @@ bundle_parse(const char *s, struct ofpbuf *ofpacts)
  * 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
-bundle_parse_load(const char *s, struct ofpbuf *ofpacts)
+bundle_parse_load(const char *s, const struct ofputil_port_map *port_map,
+                  struct ofpbuf *ofpacts)
 {
     char *fields, *basis, *algorithm, *slave_type, *dst, *slave_delim;
     char *tokstr, *save_ptr;
@@ -274,7 +278,8 @@ bundle_parse_load(const char *s, struct ofpbuf *ofpacts)
     dst = strtok_r(NULL, ", ", &save_ptr);
     slave_delim = strtok_r(NULL, ": ", &save_ptr);
 
-    error = bundle_parse__(s, &save_ptr, fields, basis, algorithm, slave_type,
+    error = bundle_parse__(s, port_map,
+                           &save_ptr, fields, basis, algorithm, slave_type,
                            dst, slave_delim, ofpacts);
 
     free(tokstr);
@@ -282,9 +287,11 @@ bundle_parse_load(const char *s, struct ofpbuf *ofpacts)
     return error;
 }
 
-/* Appends a human-readable representation of 'nab' to 's'. */
+/* Appends a human-readable representation of 'nab' to 's'.  If 'port_map' is
+ * nonnull, uses it to translate port numbers to names in output. */
 void
-bundle_format(const struct ofpact_bundle *bundle, struct ds *s)
+bundle_format(const struct ofpact_bundle *bundle,
+              const struct ofputil_port_map *port_map, struct ds *s)
 {
     const char *action, *fields, *algorithm;
     size_t i;
@@ -318,7 +325,7 @@ bundle_format(const struct ofpact_bundle *bundle, struct ds *s)
             ds_put_char(s, ',');
         }
 
-        ofputil_format_port(bundle->slaves[i], s);
+        ofputil_format_port(bundle->slaves[i], port_map, s);
     }
 
     ds_put_format(s, "%s)%s", colors.paren, colors.end);
diff --git a/lib/bundle.h b/lib/bundle.h
index 48b9b7959fb9..21f353f67f09 100644
--- a/lib/bundle.h
+++ b/lib/bundle.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, 2012, 2013, 2014 Nicira, Inc.
+/* Copyright (c) 2011, 2012, 2013, 2014, 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.
@@ -32,6 +32,7 @@ struct flow_wildcards;
 struct match;
 struct ofpact_bundle;
 struct ofpbuf;
+struct ofputil_port_map;
 
 /* NXAST_BUNDLE helper functions.
  *
@@ -45,9 +46,12 @@ ofp_port_t bundle_execute(const struct ofpact_bundle *, const struct flow *,
                         void *aux);
 enum ofperr bundle_check(const struct ofpact_bundle *, ofp_port_t max_ports,
                          const struct match *);
-char *bundle_parse(const char *, struct ofpbuf *ofpacts) OVS_WARN_UNUSED_RESULT;
-char *bundle_parse_load(const char *, struct ofpbuf *ofpacts)
+char *bundle_parse(const char *, const struct ofputil_port_map *port_map,
+                   struct ofpbuf *ofpacts) OVS_WARN_UNUSED_RESULT;
+char *bundle_parse_load(const char *, const struct ofputil_port_map *port_map,
+                        struct ofpbuf *ofpacts)
     OVS_WARN_UNUSED_RESULT;
-void bundle_format(const struct ofpact_bundle *, struct ds *);
+void bundle_format(const struct ofpact_bundle *,
+                   const struct ofputil_port_map *, struct ds *);
 
 #endif /* bundle.h */
diff --git a/lib/classifier.c b/lib/classifier.c
index 782ef071eeaa..1b652df8df34 100644
--- a/lib/classifier.c
+++ b/lib/classifier.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Nicira, Inc.
+ * Copyright (c) 2009-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.
@@ -269,9 +269,9 @@ cls_rule_equal(const struct cls_rule *a, const struct cls_rule *b)
 /* Appends a string describing 'rule' to 's'. */
 void
 cls_rule_format(const struct cls_rule *rule, const struct tun_table *tun_table,
-                struct ds *s)
+                const struct ofputil_port_map *port_map, struct ds *s)
 {
-    minimatch_format(&rule->match, tun_table, s, rule->priority);
+    minimatch_format(&rule->match, tun_table, port_map, s, rule->priority);
 }
 
 /* Returns true if 'rule' matches every packet, false otherwise. */
diff --git a/lib/classifier.h b/lib/classifier.h
index c57fa6fc469d..f0ea5a9cb8b2 100644
--- a/lib/classifier.h
+++ b/lib/classifier.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 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.
@@ -413,7 +413,7 @@ int classifier_count(const struct classifier *);
  * concurrently with modifiers and each other. */
 bool cls_rule_equal(const struct cls_rule *, const struct cls_rule *);
 void cls_rule_format(const struct cls_rule *, const struct tun_table *,
-                     struct ds *);
+                     const struct ofputil_port_map *, struct ds *);
 bool cls_rule_is_catchall(const struct cls_rule *);
 bool cls_rule_is_loose_match(const struct cls_rule *rule,
                              const struct minimatch *criteria);
diff --git a/lib/dpctl.c b/lib/dpctl.c
index 11be85706db5..1ebf5da2d471 100644
--- a/lib/dpctl.c
+++ b/lib/dpctl.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 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.
@@ -768,7 +768,6 @@ dpctl_dump_flows(int argc, const char *argv[], struct dpctl_params *dpctl_p)
     struct dpif_port_dump port_dump;
     struct dpif_port dpif_port;
     struct hmap portno_names;
-    struct simap names_portno;
 
     struct dpif_flow_dump_thread *flow_dump_thread;
     struct dpif_flow_dump *flow_dump;
@@ -794,16 +793,19 @@ dpctl_dump_flows(int argc, const char *argv[], struct dpctl_params *dpctl_p)
 
 
     hmap_init(&portno_names);
-    simap_init(&names_portno);
     DPIF_PORT_FOR_EACH (&dpif_port, &port_dump, dpif) {
         odp_portno_names_set(&portno_names, dpif_port.port_no, dpif_port.name);
-        simap_put(&names_portno, dpif_port.name,
-                  odp_to_u32(dpif_port.port_no));
     }
 
     if (filter) {
-        char *err = parse_ofp_exact_flow(&flow_filter, &wc_filter, NULL, filter,
-                                         &names_portno);
+        struct ofputil_port_map port_map;
+        ofputil_port_map_init(&port_map);
+        DPIF_PORT_FOR_EACH (&dpif_port, &port_dump, dpif) {
+            ofputil_port_map_put(&port_map, dpif_port.port_no, dpif_port.name);
+        }
+        char *err = parse_ofp_exact_flow(&flow_filter, &wc_filter, NULL,
+                                         filter, &port_map);
+        ofputil_port_map_destroy(&port_map);
         if (err) {
             dpctl_error(dpctl_p, 0, "Failed to parse filter (%s)", err);
             error = EINVAL;
@@ -867,7 +869,6 @@ dpctl_dump_flows(int argc, const char *argv[], struct dpctl_params *dpctl_p)
 
 out_dpifclose:
     odp_portno_names_destroy(&portno_names);
-    simap_destroy(&names_portno);
     hmap_destroy(&portno_names);
     dpif_close(dpif);
 out_freefilter:
diff --git a/lib/flow.c b/lib/flow.c
index 7f98a46ae737..779bb54d50a7 100644
--- a/lib/flow.c
+++ b/lib/flow.c
@@ -1066,10 +1066,11 @@ flow_clear_conntrack(struct flow *flow)
 }
 
 char *
-flow_to_string(const struct flow *flow)
+flow_to_string(const struct flow *flow,
+               const struct ofputil_port_map *port_map)
 {
     struct ds ds = DS_EMPTY_INITIALIZER;
-    flow_format(&ds, flow);
+    flow_format(&ds, flow, port_map);
     return ds_cstr(&ds);
 }
 
@@ -1309,7 +1310,8 @@ unknown:
 }
 
 void
-flow_format(struct ds *ds, const struct flow *flow)
+flow_format(struct ds *ds,
+            const struct flow *flow, const struct ofputil_port_map *port_map)
 {
     struct match match;
     struct flow_wildcards *wc = &match.wc;
@@ -1371,13 +1373,14 @@ flow_format(struct ds *ds, const struct flow *flow)
         WC_UNMASK_FIELD(wc, metadata);
     }
 
-    match_format(&match, ds, OFP_DEFAULT_PRIORITY);
+    match_format(&match, port_map, ds, OFP_DEFAULT_PRIORITY);
 }
 
 void
-flow_print(FILE *stream, const struct flow *flow)
+flow_print(FILE *stream,
+           const struct flow *flow, const struct ofputil_port_map *port_map)
 {
-    char *s = flow_to_string(flow);
+    char *s = flow_to_string(flow, port_map);
     fputs(s, stream);
     free(s);
 }
diff --git a/lib/flow.h b/lib/flow.h
index 94c8a0c4ee9e..68bd4f3c5149 100644
--- a/lib/flow.h
+++ b/lib/flow.h
@@ -37,6 +37,7 @@ struct ds;
 struct flow_wildcards;
 struct minimask;
 struct dp_packet;
+struct ofputil_port_map;
 struct pkt_metadata;
 struct match;
 
@@ -76,7 +77,7 @@ const char *ct_state_to_string(uint32_t state);
 uint32_t ct_state_from_string(const char *);
 void flow_clear_conntrack(struct flow *);
 
-char *flow_to_string(const struct flow *);
+char *flow_to_string(const struct flow *, const struct ofputil_port_map *);
 void format_flags(struct ds *ds, const char *(*bit_to_string)(uint32_t),
                   uint32_t flags, char del);
 void format_flags_masked(struct ds *ds, const char *name,
@@ -86,8 +87,9 @@ int parse_flags(const char *s, const char *(*bit_to_string)(uint32_t),
                 char end, const char *field_name, char **res_string,
                 uint32_t *res_flags, uint32_t allowed, uint32_t *res_mask);
 
-void flow_format(struct ds *, const struct flow *);
-void flow_print(FILE *, const struct flow *);
+void flow_format(struct ds *, const struct flow *,
+                 const struct ofputil_port_map *);
+void flow_print(FILE *, const struct flow *, const struct ofputil_port_map *);
 static inline int flow_compare_3way(const struct flow *, const struct flow *);
 static inline bool flow_equal(const struct flow *, const struct flow *);
 static inline size_t flow_hash(const struct flow *, uint32_t basis);
diff --git a/lib/learn.c b/lib/learn.c
index f8f872143cb4..bc5a6eb2d16f 100644
--- a/lib/learn.c
+++ b/lib/learn.c
@@ -232,6 +232,7 @@ learn_parse_load_immediate(union mf_subvalue *imm, const char *s,
  * error.  The caller is responsible for freeing the returned string. */
 static char * OVS_WARN_UNUSED_RESULT
 learn_parse_spec(const char *orig, char *name, char *value,
+                 const struct ofputil_port_map *port_map,
                  struct ofpact_learn_spec *spec,
                  struct ofpbuf *ofpacts, struct match *match)
 {
@@ -262,7 +263,8 @@ learn_parse_spec(const char *orig, char *name, char *value,
                 /* Try an immediate value. */
                 if (dst.ofs == 0 && dst.n_bits == dst.field->n_bits) {
                     /* Full field value. */
-                    imm_error = mf_parse_value(dst.field, value, &imm);
+                    imm_error = mf_parse_value(dst.field, value, port_map,
+                                               &imm);
                 } else {
                     char *tail;
                     /* Partial field value. */
@@ -279,7 +281,7 @@ learn_parse_spec(const char *orig, char *name, char *value,
                         struct ds ds;
 
                         ds_init(&ds);
-                        mf_format(dst.field, &imm, NULL, &ds);
+                        mf_format(dst.field, &imm, NULL, NULL, &ds);
                         imm_error = xasprintf("%s: value %s does not fit into %d bits",
                                               orig, ds_cstr(&ds), dst.n_bits);
                         ds_destroy(&ds);
@@ -378,7 +380,8 @@ learn_parse_spec(const char *orig, char *name, char *value,
 /* Returns NULL if successful, otherwise a malloc()'d string describing the
  * error.  The caller is responsible for freeing the returned string. */
 static char * OVS_WARN_UNUSED_RESULT
-learn_parse__(char *orig, char *arg, struct ofpbuf *ofpacts)
+learn_parse__(char *orig, char *arg, const struct ofputil_port_map *port_map,
+              struct ofpbuf *ofpacts)
 {
     struct ofpact_learn *learn;
     struct match match;
@@ -435,7 +438,8 @@ learn_parse__(char *orig, char *arg, struct ofpbuf *ofpacts)
             char *error;
 
             spec = ofpbuf_put_zeros(ofpacts, sizeof *spec);
-            error = learn_parse_spec(orig, name, value, spec, ofpacts, &match);
+            error = learn_parse_spec(orig, name, value, port_map,
+                                     spec, ofpacts, &match);
             if (error) {
                 return error;
             }
@@ -460,10 +464,11 @@ learn_parse__(char *orig, char *arg, struct ofpbuf *ofpacts)
  *
  * Modifies 'arg'. */
 char * OVS_WARN_UNUSED_RESULT
-learn_parse(char *arg, struct ofpbuf *ofpacts)
+learn_parse(char *arg, const struct ofputil_port_map *port_map,
+            struct ofpbuf *ofpacts)
 {
     char *orig = xstrdup(arg);
-    char *error = learn_parse__(orig, arg, ofpacts);
+    char *error = learn_parse__(orig, arg, port_map, ofpacts);
     free(orig);
     return error;
 }
@@ -471,7 +476,8 @@ learn_parse(char *arg, struct ofpbuf *ofpacts)
 /* Appends a description of 'learn' to 's', in the format that ovs-ofctl(8)
  * describes. */
 void
-learn_format(const struct ofpact_learn *learn, struct ds *s)
+learn_format(const struct ofpact_learn *learn,
+             const struct ofputil_port_map *port_map, struct ds *s)
 {
     const struct ofpact_learn_spec *spec;
     struct match match;
@@ -535,7 +541,7 @@ learn_format(const struct ofpact_learn *learn, struct ds *s)
                        ofpact_learn_spec_imm(spec), n_bytes);
                 ds_put_format(s, "%s%s=%s", colors.param,
                               spec->dst.field->name, colors.end);
-                mf_format(spec->dst.field, &value, NULL, s);
+                mf_format(spec->dst.field, &value, NULL, port_map, s);
             } else {
                 ds_put_format(s, "%s", colors.param);
                 mf_format_subfield(&spec->dst, s);
diff --git a/lib/learn.h b/lib/learn.h
index 9dd41d379ae9..31d3a14e827a 100644
--- a/lib/learn.h
+++ b/lib/learn.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2012, 2013, 2014 Nicira, Inc.
+ * Copyright (c) 2011, 2012, 2013, 2014, 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.
@@ -27,6 +27,7 @@ struct match;
 struct ofpbuf;
 struct ofpact_learn;
 struct ofputil_flow_mod;
+struct ofputil_port_map;
 struct nx_action_learn;
 
 /* NXAST_LEARN helper functions.
@@ -39,7 +40,10 @@ void learn_execute(const struct ofpact_learn *, const struct flow *,
                    struct ofputil_flow_mod *, struct ofpbuf *ofpacts);
 void learn_mask(const struct ofpact_learn *, struct flow_wildcards *);
 
-char *learn_parse(char *, struct ofpbuf *ofpacts) OVS_WARN_UNUSED_RESULT;
-void learn_format(const struct ofpact_learn *, struct ds *);
+char *learn_parse(char *, const struct ofputil_port_map *,
+                  struct ofpbuf *ofpacts)
+    OVS_WARN_UNUSED_RESULT;
+void learn_format(const struct ofpact_learn *,
+                  const struct ofputil_port_map *, struct ds *);
 
 #endif /* learn.h */
diff --git a/lib/learning-switch.c b/lib/learning-switch.c
index 77155d04fcc0..ed9167d26f03 100644
--- a/lib/learning-switch.c
+++ b/lib/learning-switch.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Nicira, Inc.
+ * Copyright (c) 2008-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.
@@ -368,7 +368,7 @@ lswitch_process_packet(struct lswitch *sw, const struct ofpbuf *msg)
     } else if (type == OFPTYPE_FLOW_REMOVED) {
         /* Nothing to do. */
     } else if (VLOG_IS_DBG_ENABLED()) {
-        char *s = ofp_to_string(msg->data, msg->size, 2);
+        char *s = ofp_to_string(msg->data, msg->size, NULL, 2);
         VLOG_DBG_RL(&rl, "%016llx: OpenFlow packet ignored: %s",
                     sw->datapath_id, s);
         free(s);
diff --git a/lib/match.c b/lib/match.c
index 96b1850b8c74..f3612c0c8d91 100644
--- a/lib/match.c
+++ b/lib/match.c
@@ -1156,9 +1156,12 @@ format_ct_label_masked(struct ds *s, const ovs_u128 *key, const ovs_u128 *mask)
 }
 
 /* Appends a string representation of 'match' to 's'.  If 'priority' is
- * different from OFP_DEFAULT_PRIORITY, includes it in 's'. */
+ * different from OFP_DEFAULT_PRIORITY, includes it in 's'.  If 'port_map' is
+ * nonnull, uses it to translate port numbers to names in output. */
 void
-match_format(const struct match *match, struct ds *s, int priority)
+match_format(const struct match *match,
+             const struct ofputil_port_map *port_map,
+             struct ds *s, int priority)
 {
     const struct flow_wildcards *wc = &match->wc;
     size_t start_len = s->length;
@@ -1200,7 +1203,7 @@ match_format(const struct match *match, struct ds *s, int priority)
 
     if (wc->masks.actset_output) {
         ds_put_format(s, "%sactset_output=%s", colors.param, colors.end);
-        ofputil_format_port(f->actset_output, s);
+        ofputil_format_port(f->actset_output, port_map, s);
         ds_put_char(s, ',');
     }
 
@@ -1313,7 +1316,7 @@ match_format(const struct match *match, struct ds *s, int priority)
 
     if (wc->masks.in_port.ofp_port) {
         ds_put_format(s, "%sin_port=%s", colors.param, colors.end);
-        ofputil_format_port(f->in_port.ofp_port, s);
+        ofputil_format_port(f->in_port.ofp_port, port_map, s);
         ds_put_char(s, ',');
     }
     for (i = 0; i < FLOW_MAX_VLAN_HEADERS; i++) {
@@ -1482,20 +1485,23 @@ match_format(const struct match *match, struct ds *s, int priority)
 }
 
 /* Converts 'match' to a string and returns the string.  If 'priority' is
- * different from OFP_DEFAULT_PRIORITY, includes it in the string.  The caller
- * must free the string (with free()). */
+ * different from OFP_DEFAULT_PRIORITY, includes it in the string.  If
+ * 'port_map' is nonnull, uses it to translate port numbers to names in
+ * output.  The caller must free the string (with free()). */
 char *
-match_to_string(const struct match *match, int priority)
+match_to_string(const struct match *match,
+                const struct ofputil_port_map *port_map, int priority)
 {
     struct ds s = DS_EMPTY_INITIALIZER;
-    match_format(match, &s, priority);
+    match_format(match, port_map, &s, priority);
     return ds_steal_cstr(&s);
 }
 
 void
-match_print(const struct match *match)
+match_print(const struct match *match,
+            const struct ofputil_port_map *port_map)
 {
-    char *s = match_to_string(match, OFP_DEFAULT_PRIORITY);
+    char *s = match_to_string(match, port_map, OFP_DEFAULT_PRIORITY);
     puts(s);
     free(s);
 }
@@ -1586,27 +1592,32 @@ minimatch_matches_flow(const struct minimatch *match,
 }
 
 /* Appends a string representation of 'match' to 's'.  If 'priority' is
- * different from OFP_DEFAULT_PRIORITY, includes it in 's'. */
+ * different from OFP_DEFAULT_PRIORITY, includes it in 's'.  If 'port_map' is
+ * nonnull, uses it to translate port numbers to names in output. */
 void
 minimatch_format(const struct minimatch *match,
-                 const struct tun_table *tun_table,struct ds *s, int priority)
+                 const struct tun_table *tun_table,
+                 const struct ofputil_port_map *port_map,
+                 struct ds *s, int priority)
 {
     struct match megamatch;
 
     minimatch_expand(match, &megamatch);
     megamatch.flow.tunnel.metadata.tab = tun_table;
 
-    match_format(&megamatch, s, priority);
+    match_format(&megamatch, port_map, s, priority);
 }
 
 /* Converts 'match' to a string and returns the string.  If 'priority' is
  * different from OFP_DEFAULT_PRIORITY, includes it in the string.  The caller
- * must free the string (with free()). */
+ * must free the string (with free()).  If 'port_map' is nonnull, uses it to
+ * translate port numbers to names in output. */
 char *
-minimatch_to_string(const struct minimatch *match, int priority)
+minimatch_to_string(const struct minimatch *match,
+                    const struct ofputil_port_map *port_map, int priority)
 {
     struct match megamatch;
 
     minimatch_expand(match, &megamatch);
-    return match_to_string(&megamatch, priority);
+    return match_to_string(&megamatch, port_map, priority);
 }
diff --git a/lib/meta-flow.c b/lib/meta-flow.c
index 6b97794f1753..0667889c280c 100644
--- a/lib/meta-flow.c
+++ b/lib/meta-flow.c
@@ -2336,28 +2336,30 @@ mf_from_ipv6_string(const struct mf_field *mf, const char *s,
 
 static char *
 mf_from_ofp_port_string(const struct mf_field *mf, const char *s,
+                        const struct ofputil_port_map *port_map,
                         ovs_be16 *valuep, ovs_be16 *maskp)
 {
     ofp_port_t port;
 
     ovs_assert(mf->n_bytes == sizeof(ovs_be16));
 
-    if (ofputil_port_from_string(s, &port)) {
+    if (ofputil_port_from_string(s, port_map, &port)) {
         *valuep = htons(ofp_to_u16(port));
         *maskp = OVS_BE16_MAX;
         return NULL;
     }
-    return xasprintf("%s: port value out of range for %s", s, mf->name);
+    return xasprintf("%s: invalid or unknown port for %s", s, mf->name);
 }
 
 static char *
 mf_from_ofp_port_string32(const struct mf_field *mf, const char *s,
+                          const struct ofputil_port_map *port_map,
                           ovs_be32 *valuep, ovs_be32 *maskp)
 {
     ofp_port_t port;
 
     ovs_assert(mf->n_bytes == sizeof(ovs_be32));
-    if (ofputil_port_from_string(s, &port)) {
+    if (ofputil_port_from_string(s, port_map, &port)) {
         *valuep = ofputil_port_to_ofp11(port);
         *maskp = OVS_BE32_MAX;
         return NULL;
@@ -2470,6 +2472,7 @@ mf_from_ct_state_string(const char *s, ovs_be32 *flagsp, ovs_be32 *maskp)
  * NULL if successful, otherwise a malloc()'d string describing the error. */
 char *
 mf_parse(const struct mf_field *mf, const char *s,
+         const struct ofputil_port_map *port_map,
          union mf_value *value, union mf_value *mask)
 {
     char *error;
@@ -2505,11 +2508,13 @@ mf_parse(const struct mf_field *mf, const char *s,
         break;
 
     case MFS_OFP_PORT:
-        error = mf_from_ofp_port_string(mf, s, &value->be16, &mask->be16);
+        error = mf_from_ofp_port_string(mf, s, port_map,
+                                        &value->be16, &mask->be16);
         break;
 
     case MFS_OFP_PORT_OXM:
-        error = mf_from_ofp_port_string32(mf, s, &value->be32, &mask->be32);
+        error = mf_from_ofp_port_string32(mf, s, port_map,
+                                          &value->be32, &mask->be32);
         break;
 
     case MFS_FRAG:
@@ -2539,12 +2544,13 @@ mf_parse(const struct mf_field *mf, const char *s,
 /* Parses 's', a string value for field 'mf', into 'value'.  Returns NULL if
  * successful, otherwise a malloc()'d string describing the error. */
 char *
-mf_parse_value(const struct mf_field *mf, const char *s, union mf_value *value)
+mf_parse_value(const struct mf_field *mf, const char *s,
+               const struct ofputil_port_map *port_map, union mf_value *value)
 {
     union mf_value mask;
     char *error;
 
-    error = mf_parse(mf, s, value, &mask);
+    error = mf_parse(mf, s, port_map, value, &mask);
     if (error) {
         return error;
     }
@@ -2624,6 +2630,7 @@ mf_format_ct_state_string(ovs_be32 value, ovs_be32 mask, struct ds *s)
 void
 mf_format(const struct mf_field *mf,
           const union mf_value *value, const union mf_value *mask,
+          const struct ofputil_port_map *port_map,
           struct ds *s)
 {
     if (mask) {
@@ -2640,13 +2647,13 @@ mf_format(const struct mf_field *mf,
         if (!mask) {
             ofp_port_t port;
             ofputil_port_from_ofp11(value->be32, &port);
-            ofputil_format_port(port, s);
+            ofputil_format_port(port, port_map, s);
             break;
         }
         /* fall through */
     case MFS_OFP_PORT:
         if (!mask) {
-            ofputil_format_port(u16_to_ofp(ntohs(value->be16)), s);
+            ofputil_format_port(u16_to_ofp(ntohs(value->be16)), port_map, s);
             break;
         }
         /* fall through */
diff --git a/lib/nx-match.c b/lib/nx-match.c
index 68e58d393198..79b1c4a7dbd6 100644
--- a/lib/nx-match.c
+++ b/lib/nx-match.c
@@ -1248,7 +1248,7 @@ nx_format_mask_tlv(struct ds *ds, enum mf_field_id id,
 
     if (!is_all_ones(mask, mf->n_bytes)) {
         ds_put_char(ds, '=');
-        mf_format(mf, mask, NULL, ds);
+        mf_format(mf, mask, NULL, NULL, ds);
     }
 
     ds_put_char(ds, ',');
diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
index 61e98601f117..22daacad0927 100644
--- a/lib/ofp-actions.c
+++ b/lib/ofp-actions.c
@@ -403,7 +403,8 @@ static void *ofpact_put_raw(struct ofpbuf *, enum ofp_version,
                             enum ofp_raw_action_type, uint64_t arg);
 
 static char *OVS_WARN_UNUSED_RESULT ofpacts_parse(
-    char *str, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols,
+    char *str, const struct ofputil_port_map *,
+    struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols,
     bool allow_instructions, enum ofpact_type outer_action);
 static enum ofperr ofpacts_pull_openflow_actions__(
     struct ofpbuf *openflow, unsigned int actions_len,
@@ -411,7 +412,7 @@ static enum ofperr ofpacts_pull_openflow_actions__(
     struct ofpbuf *ofpacts, enum ofpact_type outer_action,
     const struct vl_mff_map *vl_mff_map, uint64_t *ofpacts_tlv_bitmap);
 static char * OVS_WARN_UNUSED_RESULT ofpacts_parse_copy(
-    const char *s_, struct ofpbuf *ofpacts,
+    const char *s_, const struct ofputil_port_map *, struct ofpbuf *ofpacts,
     enum ofputil_protocol *usable_protocols,
     bool allow_instructions, enum ofpact_type outer_action);
 
@@ -589,14 +590,16 @@ encode_OUTPUT(const struct ofpact_output *output,
 
 static char * OVS_WARN_UNUSED_RESULT
 parse_truncate_subfield(struct ofpact_output_trunc *output_trunc,
-                        const char *arg_)
+                        const char *arg_,
+                        const struct ofputil_port_map *port_map)
 {
     char *key, *value;
     char *arg = CONST_CAST(char *, arg_);
 
     while (ofputil_parse_key_value(&arg, &key, &value)) {
         if (!strcmp(key, "port")) {
-            if (!ofputil_port_from_string(value, &output_trunc->port)) {
+            if (!ofputil_port_from_string(value, port_map,
+                                          &output_trunc->port)) {
                 return xasprintf("output to unknown truncate port: %s",
                                   value);
             }
@@ -622,14 +625,16 @@ parse_truncate_subfield(struct ofpact_output_trunc *output_trunc,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_OUTPUT(const char *arg, struct ofpbuf *ofpacts,
+parse_OUTPUT(const char *arg,
+             const struct ofputil_port_map *port_map,
+             struct ofpbuf *ofpacts,
              enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
     if (strstr(arg, "port") && strstr(arg, "max_len")) {
         struct ofpact_output_trunc *output_trunc;
 
         output_trunc = ofpact_put_OUTPUT_TRUNC(ofpacts);
-        return parse_truncate_subfield(output_trunc, arg);
+        return parse_truncate_subfield(output_trunc, arg, port_map);
     } else {
         struct mf_subfield src;
         char *error = mf_parse_subfield(&src, arg);
@@ -644,7 +649,7 @@ parse_OUTPUT(const char *arg, struct ofpbuf *ofpacts,
             struct ofpact_output *output;
 
             output = ofpact_put_OUTPUT(ofpacts);
-            if (!ofputil_port_from_string(arg, &output->port)) {
+            if (!ofputil_port_from_string(arg, port_map, &output->port)) {
                 return xasprintf("%s: output to unknown port", arg);
             }
             output->max_len = output->port == OFPP_CONTROLLER ? UINT16_MAX : 0;
@@ -654,16 +659,15 @@ parse_OUTPUT(const char *arg, struct ofpbuf *ofpacts,
 }
 
 static void
-format_OUTPUT(const struct ofpact_output *a, struct ds *s)
+format_OUTPUT(const struct ofpact_output *a,
+              const struct ofputil_port_map *port_map, struct ds *s)
 {
     if (ofp_to_u16(a->port) < ofp_to_u16(OFPP_MAX)) {
-        ds_put_format(s, "%soutput:%s%"PRIu32,
-                      colors.special, colors.end, a->port);
-    } else {
-        ofputil_format_port(a->port, s);
-        if (a->port == OFPP_CONTROLLER) {
-            ds_put_format(s, ":%"PRIu16, a->max_len);
-        }
+        ds_put_format(s, "%soutput:%s", colors.special, colors.end);
+    }
+    ofputil_format_port(a->port, port_map, s);
+    if (a->port == OFPP_CONTROLLER) {
+        ds_put_format(s, ":%"PRIu16, a->max_len);
     }
 }
 
@@ -686,14 +690,18 @@ encode_GROUP(const struct ofpact_group *group,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_GROUP(char *arg, struct ofpbuf *ofpacts,
-                    enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_GROUP(char *arg,
+            const struct ofputil_port_map *port_map OVS_UNUSED,
+            struct ofpbuf *ofpacts,
+            enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
     return str_to_u32(arg, &ofpact_put_GROUP(ofpacts)->group_id);
 }
 
 static void
-format_GROUP(const struct ofpact_group *a, struct ds *s)
+format_GROUP(const struct ofpact_group *a,
+             const struct ofputil_port_map *port_map OVS_UNUSED,
+             struct ds *s)
 {
     ds_put_format(s, "%sgroup:%s%"PRIu32,
                   colors.special, colors.end, a->group_id);
@@ -863,8 +871,10 @@ encode_CONTROLLER(const struct ofpact_controller *controller,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_CONTROLLER(char *arg, struct ofpbuf *ofpacts,
-                  enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_CONTROLLER(char *arg,
+                 const struct ofputil_port_map *port_map OVS_UNUSED,
+                 struct ofpbuf *ofpacts,
+                 enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
     enum ofp_packet_in_reason reason = OFPR_ACTION;
     uint16_t controller_id = 0;
@@ -952,7 +962,9 @@ format_hex_arg(struct ds *s, const uint8_t *data, size_t len)
 }
 
 static void
-format_CONTROLLER(const struct ofpact_controller *a, struct ds *s)
+format_CONTROLLER(const struct ofpact_controller *a,
+                  const struct ofputil_port_map *port_map OVS_UNUSED,
+                  struct ds *s)
 {
     if (a->reason == OFPR_ACTION && !a->controller_id && !a->userdata_len
         && !a->pause) {
@@ -1042,7 +1054,9 @@ encode_ENQUEUE(const struct ofpact_enqueue *enqueue,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_ENQUEUE(char *arg, struct ofpbuf *ofpacts,
+parse_ENQUEUE(char *arg,
+              const struct ofputil_port_map *port_map,
+              struct ofpbuf *ofpacts,
               enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
     char *sp = NULL;
@@ -1056,17 +1070,18 @@ parse_ENQUEUE(char *arg, struct ofpbuf *ofpacts,
     }
 
     enqueue = ofpact_put_ENQUEUE(ofpacts);
-    if (!ofputil_port_from_string(port, &enqueue->port)) {
+    if (!ofputil_port_from_string(port, port_map, &enqueue->port)) {
         return xasprintf("%s: enqueue to unknown port", port);
     }
     return str_to_u32(queue, &enqueue->queue);
 }
 
 static void
-format_ENQUEUE(const struct ofpact_enqueue *a, struct ds *s)
+format_ENQUEUE(const struct ofpact_enqueue *a,
+               const struct ofputil_port_map *port_map, struct ds *s)
 {
     ds_put_format(s, "%senqueue:%s", colors.param, colors.end);
-    ofputil_format_port(a->port, s);
+    ofputil_format_port(a->port, port_map, s);
     ds_put_format(s, ":%"PRIu32, a->queue);
 }
 
@@ -1210,14 +1225,18 @@ encode_OUTPUT_REG(const struct ofpact_output_reg *output_reg,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_OUTPUT_REG(const char *arg, struct ofpbuf *ofpacts,
+parse_OUTPUT_REG(const char *arg,
+                 const struct ofputil_port_map *port_map,
+                 struct ofpbuf *ofpacts,
                  enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
-    return parse_OUTPUT(arg, ofpacts, usable_protocols);
+    return parse_OUTPUT(arg, port_map, ofpacts, usable_protocols);
 }
 
 static void
-format_OUTPUT_REG(const struct ofpact_output_reg *a, struct ds *s)
+format_OUTPUT_REG(const struct ofpact_output_reg *a,
+                  const struct ofputil_port_map *port_map OVS_UNUSED,
+                  struct ds *s)
 {
     ds_put_format(s, "%soutput:%s", colors.special, colors.end);
     mf_format_subfield(&a->src, s);
@@ -1423,22 +1442,25 @@ encode_BUNDLE(const struct ofpact_bundle *bundle,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_BUNDLE(const char *arg, struct ofpbuf *ofpacts,
+parse_BUNDLE(const char *arg, const struct ofputil_port_map *port_map,
+             struct ofpbuf *ofpacts,
              enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
-    return bundle_parse(arg, ofpacts);
+    return bundle_parse(arg, port_map, ofpacts);
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_bundle_load(const char *arg, struct ofpbuf *ofpacts)
+parse_bundle_load(const char *arg, const struct ofputil_port_map *port_map,
+                  struct ofpbuf *ofpacts)
 {
-    return bundle_parse_load(arg, ofpacts);
+    return bundle_parse_load(arg, port_map, ofpacts);
 }
 
 static void
-format_BUNDLE(const struct ofpact_bundle *a, struct ds *s)
+format_BUNDLE(const struct ofpact_bundle *a,
+              const struct ofputil_port_map *port_map, struct ds *s)
 {
-    bundle_format(a, s);
+    bundle_format(a, port_map, s);
 }
 
 /* Set VLAN actions. */
@@ -1496,7 +1518,8 @@ encode_SET_VLAN_VID(const struct ofpact_vlan_vid *vlan_vid,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_set_vlan_vid(char *arg, struct ofpbuf *ofpacts, bool push_vlan_if_needed)
+parse_set_vlan_vid(char *arg,
+                   struct ofpbuf *ofpacts, bool push_vlan_if_needed)
 {
     struct ofpact_vlan_vid *vlan_vid;
     uint16_t vid;
@@ -1517,14 +1540,18 @@ parse_set_vlan_vid(char *arg, struct ofpbuf *ofpacts, bool push_vlan_if_needed)
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_SET_VLAN_VID(char *arg, struct ofpbuf *ofpacts,
+parse_SET_VLAN_VID(char *arg,
+                   const struct ofputil_port_map *port_map OVS_UNUSED,
+                   struct ofpbuf *ofpacts,
                    enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
     return parse_set_vlan_vid(arg, ofpacts, false);
 }
 
 static void
-format_SET_VLAN_VID(const struct ofpact_vlan_vid *a, struct ds *s)
+format_SET_VLAN_VID(const struct ofpact_vlan_vid *a,
+                    const struct ofputil_port_map *port_map OVS_UNUSED,
+                    struct ds *s)
 {
     ds_put_format(s, "%s%s:%s%"PRIu16, colors.param,
                   a->push_vlan_if_needed ? "mod_vlan_vid" : "set_vlan_vid",
@@ -1586,7 +1613,8 @@ encode_SET_VLAN_PCP(const struct ofpact_vlan_pcp *vlan_pcp,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_set_vlan_pcp(char *arg, struct ofpbuf *ofpacts, bool push_vlan_if_needed)
+parse_set_vlan_pcp(char *arg,
+                   struct ofpbuf *ofpacts, bool push_vlan_if_needed)
 {
     struct ofpact_vlan_pcp *vlan_pcp;
     uint8_t pcp;
@@ -1607,14 +1635,18 @@ parse_set_vlan_pcp(char *arg, struct ofpbuf *ofpacts, bool push_vlan_if_needed)
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_SET_VLAN_PCP(char *arg, struct ofpbuf *ofpacts,
+parse_SET_VLAN_PCP(char *arg,
+                   const struct ofputil_port_map *port_map OVS_UNUSED,
+                   struct ofpbuf *ofpacts,
                    enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
     return parse_set_vlan_pcp(arg, ofpacts, false);
 }
 
 static void
-format_SET_VLAN_PCP(const struct ofpact_vlan_pcp *a, struct ds *s)
+format_SET_VLAN_PCP(const struct ofpact_vlan_pcp *a,
+                    const struct ofputil_port_map *port_map OVS_UNUSED,
+                    struct ds *s)
 {
     ds_put_format(s, "%s%s:%s%"PRIu8, colors.param,
                   a->push_vlan_if_needed ? "mod_vlan_pcp" : "set_vlan_pcp",
@@ -1649,7 +1681,9 @@ encode_STRIP_VLAN(const struct ofpact_null *null OVS_UNUSED,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_STRIP_VLAN(char *arg OVS_UNUSED, struct ofpbuf *ofpacts,
+parse_STRIP_VLAN(char *arg OVS_UNUSED,
+                 const struct ofputil_port_map *port_map OVS_UNUSED,
+                 struct ofpbuf *ofpacts,
                  enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
     ofpact_put_STRIP_VLAN(ofpacts)->ofpact.raw = OFPAT_RAW10_STRIP_VLAN;
@@ -1664,7 +1698,9 @@ parse_pop_vlan(struct ofpbuf *ofpacts)
 }
 
 static void
-format_STRIP_VLAN(const struct ofpact_null *a, struct ds *s)
+format_STRIP_VLAN(const struct ofpact_null *a,
+                  const struct ofputil_port_map *port_map OVS_UNUSED,
+                  struct ds *s)
 {
     ds_put_format(s, (a->ofpact.raw == OFPAT_RAW11_POP_VLAN
                     ? "%spop_vlan%s"
@@ -1701,7 +1737,9 @@ encode_PUSH_VLAN(const struct ofpact_push_vlan *push_vlan,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_PUSH_VLAN(char *arg, struct ofpbuf *ofpacts,
+parse_PUSH_VLAN(char *arg,
+                const struct ofputil_port_map *port_map OVS_UNUSED,
+                struct ofpbuf *ofpacts,
                 enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
     struct ofpact_push_vlan *push_vlan;
@@ -1723,7 +1761,9 @@ parse_PUSH_VLAN(char *arg, struct ofpbuf *ofpacts,
 }
 
 static void
-format_PUSH_VLAN(const struct ofpact_push_vlan *push_vlan, struct ds *s)
+format_PUSH_VLAN(const struct ofpact_push_vlan *push_vlan,
+                 const struct ofputil_port_map *port_map OVS_UNUSED,
+                 struct ds *s)
 {
     ds_put_format(s, "%spush_vlan:%s%#"PRIx16,
                   colors.param, colors.end, ntohs(push_vlan->ethertype));
@@ -1791,28 +1831,36 @@ encode_SET_ETH_DST(const struct ofpact_mac *mac,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_SET_ETH_SRC(char *arg, struct ofpbuf *ofpacts,
-                 enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_SET_ETH_SRC(char *arg,
+                  const struct ofputil_port_map *port_map OVS_UNUSED,
+                  struct ofpbuf *ofpacts,
+                  enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
     return str_to_mac(arg, &ofpact_put_SET_ETH_SRC(ofpacts)->mac);
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_SET_ETH_DST(char *arg, struct ofpbuf *ofpacts,
-                 enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_SET_ETH_DST(char *arg,
+                  const struct ofputil_port_map *port_map OVS_UNUSED,
+                  struct ofpbuf *ofpacts,
+                  enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
     return str_to_mac(arg, &ofpact_put_SET_ETH_DST(ofpacts)->mac);
 }
 
 static void
-format_SET_ETH_SRC(const struct ofpact_mac *a, struct ds *s)
+format_SET_ETH_SRC(const struct ofpact_mac *a,
+                   const struct ofputil_port_map *port_map OVS_UNUSED,
+                   struct ds *s)
 {
     ds_put_format(s, "%smod_dl_src:%s"ETH_ADDR_FMT,
                   colors.param, colors.end, ETH_ADDR_ARGS(a->mac));
 }
 
 static void
-format_SET_ETH_DST(const struct ofpact_mac *a, struct ds *s)
+format_SET_ETH_DST(const struct ofpact_mac *a,
+                   const struct ofputil_port_map *port_map OVS_UNUSED,
+                   struct ds *s)
 {
     ds_put_format(s, "%smod_dl_dst:%s"ETH_ADDR_FMT,
                   colors.param, colors.end, ETH_ADDR_ARGS(a->mac));
@@ -1869,28 +1917,36 @@ encode_SET_IPV4_DST(const struct ofpact_ipv4 *ipv4,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_SET_IPV4_SRC(char *arg, struct ofpbuf *ofpacts,
+parse_SET_IPV4_SRC(char *arg,
+                   const struct ofputil_port_map *port_map OVS_UNUSED,
+                   struct ofpbuf *ofpacts,
                    enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
     return str_to_ip(arg, &ofpact_put_SET_IPV4_SRC(ofpacts)->ipv4);
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_SET_IPV4_DST(char *arg, struct ofpbuf *ofpacts,
+parse_SET_IPV4_DST(char *arg,
+                   const struct ofputil_port_map *port_map OVS_UNUSED,
+                   struct ofpbuf *ofpacts,
                    enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
     return str_to_ip(arg, &ofpact_put_SET_IPV4_DST(ofpacts)->ipv4);
 }
 
 static void
-format_SET_IPV4_SRC(const struct ofpact_ipv4 *a, struct ds *s)
+format_SET_IPV4_SRC(const struct ofpact_ipv4 *a,
+                    const struct ofputil_port_map *port_map OVS_UNUSED,
+                    struct ds *s)
 {
     ds_put_format(s, "%smod_nw_src:%s"IP_FMT,
                   colors.param, colors.end, IP_ARGS(a->ipv4));
 }
 
 static void
-format_SET_IPV4_DST(const struct ofpact_ipv4 *a, struct ds *s)
+format_SET_IPV4_DST(const struct ofpact_ipv4 *a,
+                    const struct ofputil_port_map *port_map OVS_UNUSED,
+                    struct ds *s)
 {
     ds_put_format(s, "%smod_nw_dst:%s"IP_FMT,
                   colors.param, colors.end, IP_ARGS(a->ipv4));
@@ -1923,8 +1979,10 @@ encode_SET_IP_DSCP(const struct ofpact_dscp *dscp,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_SET_IP_DSCP(char *arg, struct ofpbuf *ofpacts,
-                 enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_SET_IP_DSCP(char *arg,
+                  const struct ofputil_port_map *port_map OVS_UNUSED,
+                  struct ofpbuf *ofpacts,
+                  enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
     uint8_t tos;
     char *error;
@@ -1942,7 +2000,9 @@ parse_SET_IP_DSCP(char *arg, struct ofpbuf *ofpacts,
 }
 
 static void
-format_SET_IP_DSCP(const struct ofpact_dscp *a, struct ds *s)
+format_SET_IP_DSCP(const struct ofpact_dscp *a,
+                   const struct ofputil_port_map *port_map OVS_UNUSED,
+                   struct ds *s)
 {
     ds_put_format(s, "%smod_nw_tos:%s%d", colors.param, colors.end, a->dscp);
 }
@@ -1979,7 +2039,9 @@ encode_SET_IP_ECN(const struct ofpact_ecn *ip_ecn,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_SET_IP_ECN(char *arg, struct ofpbuf *ofpacts,
+parse_SET_IP_ECN(char *arg,
+                 const struct ofputil_port_map *port_map OVS_UNUSED,
+                 struct ofpbuf *ofpacts,
                  enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
     uint8_t ecn;
@@ -1998,7 +2060,9 @@ parse_SET_IP_ECN(char *arg, struct ofpbuf *ofpacts,
 }
 
 static void
-format_SET_IP_ECN(const struct ofpact_ecn *a, struct ds *s)
+format_SET_IP_ECN(const struct ofpact_ecn *a,
+                  const struct ofputil_port_map *port_map OVS_UNUSED,
+                  struct ds *s)
 {
     ds_put_format(s, "%smod_nw_ecn:%s%d",
                   colors.param, colors.end, a->ecn);
@@ -2029,8 +2093,10 @@ encode_SET_IP_TTL(const struct ofpact_ip_ttl *ttl,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_SET_IP_TTL(char *arg, struct ofpbuf *ofpacts,
-                  enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_SET_IP_TTL(char *arg,
+                 const struct ofputil_port_map *port_map OVS_UNUSED,
+                 struct ofpbuf *ofpacts,
+                 enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
     uint8_t ttl;
     char *error;
@@ -2045,7 +2111,9 @@ parse_SET_IP_TTL(char *arg, struct ofpbuf *ofpacts,
 }
 
 static void
-format_SET_IP_TTL(const struct ofpact_ip_ttl *a, struct ds *s)
+format_SET_IP_TTL(const struct ofpact_ip_ttl *a,
+                  const struct ofputil_port_map *port_map OVS_UNUSED,
+                  struct ds *s)
 {
     ds_put_format(s, "%smod_nw_ttl:%s%d", colors.param, colors.end, a->ttl);
 }
@@ -2112,7 +2180,9 @@ encode_SET_L4_DST_PORT(const struct ofpact_l4_port *l4_port,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_SET_L4_SRC_PORT(char *arg, struct ofpbuf *ofpacts,
+parse_SET_L4_SRC_PORT(char *arg,
+                      const struct ofputil_port_map *port_map OVS_UNUSED,
+                      struct ofpbuf *ofpacts,
                       enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
     return str_to_u16(arg, "source port",
@@ -2120,7 +2190,9 @@ parse_SET_L4_SRC_PORT(char *arg, struct ofpbuf *ofpacts,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_SET_L4_DST_PORT(char *arg, struct ofpbuf *ofpacts,
+parse_SET_L4_DST_PORT(char *arg,
+                      const struct ofputil_port_map *port_map OVS_UNUSED,
+                      struct ofpbuf *ofpacts,
                       enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
     return str_to_u16(arg, "destination port",
@@ -2128,13 +2200,17 @@ parse_SET_L4_DST_PORT(char *arg, struct ofpbuf *ofpacts,
 }
 
 static void
-format_SET_L4_SRC_PORT(const struct ofpact_l4_port *a, struct ds *s)
+format_SET_L4_SRC_PORT(const struct ofpact_l4_port *a,
+                       const struct ofputil_port_map *port_map OVS_UNUSED,
+                       struct ds *s)
 {
     ds_put_format(s, "%smod_tp_src:%s%d", colors.param, colors.end, a->port);
 }
 
 static void
-format_SET_L4_DST_PORT(const struct ofpact_l4_port *a, struct ds *s)
+format_SET_L4_DST_PORT(const struct ofpact_l4_port *a,
+                       const struct ofputil_port_map *port_map OVS_UNUSED,
+                       struct ds *s)
 {
     ds_put_format(s, "%smod_tp_dst:%s%d", colors.param, colors.end, a->port);
 }
@@ -2424,7 +2500,9 @@ encode_REG_MOVE(const struct ofpact_reg_move *move,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_REG_MOVE(const char *arg, struct ofpbuf *ofpacts,
+parse_REG_MOVE(const char *arg,
+               const struct ofputil_port_map *port_map OVS_UNUSED,
+               struct ofpbuf *ofpacts,
                enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
     struct ofpact_reg_move *move = ofpact_put_REG_MOVE(ofpacts);
@@ -2432,7 +2510,9 @@ parse_REG_MOVE(const char *arg, struct ofpbuf *ofpacts,
 }
 
 static void
-format_REG_MOVE(const struct ofpact_reg_move *a, struct ds *s)
+format_REG_MOVE(const struct ofpact_reg_move *a,
+                const struct ofputil_port_map *port_map OVS_UNUSED,
+                struct ds *s)
 {
     nxm_format_reg_move(a, s);
 }
@@ -2544,7 +2624,7 @@ decode_ofpat_set_field(const struct ofp12_action_set_field *oasf,
             && (!(mask.be16 & htons(OFPVID12_PRESENT))
                 || !(value.be16 & htons(OFPVID12_PRESENT))))) {
         struct ds ds = DS_EMPTY_INITIALIZER;
-        mf_format(field, &value, NULL, &ds);
+        mf_format(field, &value, NULL, NULL, &ds);
         VLOG_WARN_RL(&rl, "Invalid value for set field %s: %s",
                      field->name, ds_cstr(&ds));
         ds_destroy(&ds);
@@ -2932,7 +3012,8 @@ set_field_split_str(char *arg, char **key, char **value, char **delim)
  * Returns NULL if successful, otherwise a malloc()'d string describing the
  * error.  The caller is responsible for freeing the returned string. */
 static char * OVS_WARN_UNUSED_RESULT
-set_field_parse__(char *arg, struct ofpbuf *ofpacts,
+set_field_parse__(char *arg, const struct ofputil_port_map *port_map,
+                  struct ofpbuf *ofpacts,
                   enum ofputil_protocol *usable_protocols)
 {
     char *value;
@@ -2956,7 +3037,7 @@ set_field_parse__(char *arg, struct ofpbuf *ofpacts,
     }
 
     delim[0] = '\0';
-    error = mf_parse(mf, value, &sf_value, &sf_mask);
+    error = mf_parse(mf, value, port_map, &sf_value, &sf_mask);
     if (error) {
         return error;
     }
@@ -2977,11 +3058,12 @@ set_field_parse__(char *arg, struct ofpbuf *ofpacts,
  * Returns NULL if successful, otherwise a malloc()'d string describing the
  * error.  The caller is responsible for freeing the returned string. */
 static char * OVS_WARN_UNUSED_RESULT
-parse_SET_FIELD(const char *arg, struct ofpbuf *ofpacts,
+parse_SET_FIELD(const char *arg, const struct ofputil_port_map *port_map,
+                struct ofpbuf *ofpacts,
                 enum ofputil_protocol *usable_protocols)
 {
     char *copy = xstrdup(arg);
-    char *error = set_field_parse__(copy, ofpacts, usable_protocols);
+    char *error = set_field_parse__(copy, port_map, ofpacts, usable_protocols);
     free(copy);
     return error;
 }
@@ -3014,7 +3096,7 @@ parse_reg_load(char *arg, struct ofpbuf *ofpacts)
         struct ds ds;
 
         ds_init(&ds);
-        mf_format(dst.field, &value, NULL, &ds);
+        mf_format(dst.field, &value, NULL, NULL, &ds);
         error = xasprintf("%s: value %s does not fit into %d bits",
                           arg, ds_cstr(&ds), dst.n_bits);
         ds_destroy(&ds);
@@ -3032,7 +3114,9 @@ parse_reg_load(char *arg, struct ofpbuf *ofpacts)
 }
 
 static void
-format_SET_FIELD(const struct ofpact_set_field *a, struct ds *s)
+format_SET_FIELD(const struct ofpact_set_field *a,
+                 const struct ofputil_port_map *port_map,
+                 struct ds *s)
 {
     if (a->ofpact.raw == NXAST_RAW_REG_LOAD) {
         struct mf_subfield dst;
@@ -3049,7 +3133,7 @@ format_SET_FIELD(const struct ofpact_set_field *a, struct ds *s)
         ds_chomp(s, ',');
     } else {
         ds_put_format(s, "%sset_field:%s", colors.special, colors.end);
-        mf_format(a->field, a->value, ofpact_set_field_mask(a), s);
+        mf_format(a->field, a->value, ofpact_set_field_mask(a), port_map, s);
         ds_put_format(s, "%s->%s%s",
                       colors.special, colors.end, a->field->name);
     }
@@ -3215,27 +3299,35 @@ encode_STACK_POP(const struct ofpact_stack *stack,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_STACK_PUSH(char *arg, struct ofpbuf *ofpacts,
+parse_STACK_PUSH(char *arg,
+                 const struct ofputil_port_map *port_map OVS_UNUSED,
+                 struct ofpbuf *ofpacts,
                  enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
     return nxm_parse_stack_action(ofpact_put_STACK_PUSH(ofpacts), arg);
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_STACK_POP(char *arg, struct ofpbuf *ofpacts,
+parse_STACK_POP(char *arg,
+                const struct ofputil_port_map *port_map OVS_UNUSED,
+                struct ofpbuf *ofpacts,
                 enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
     return nxm_parse_stack_action(ofpact_put_STACK_POP(ofpacts), arg);
 }
 
 static void
-format_STACK_PUSH(const struct ofpact_stack *a, struct ds *s)
+format_STACK_PUSH(const struct ofpact_stack *a,
+                  const struct ofputil_port_map *port_map OVS_UNUSED,
+                  struct ds *s)
 {
     nxm_format_stack_push(a, s);
 }
 
 static void
-format_STACK_POP(const struct ofpact_stack *a, struct ds *s)
+format_STACK_POP(const struct ofpact_stack *a,
+                 const struct ofputil_port_map *port_map OVS_UNUSED,
+                 struct ds *s)
 {
     nxm_format_stack_pop(a, s);
 }
@@ -3358,7 +3450,9 @@ parse_noargs_dec_ttl(struct ofpbuf *ofpacts)
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_DEC_TTL(char *arg, struct ofpbuf *ofpacts,
+parse_DEC_TTL(char *arg,
+              const struct ofputil_port_map *port_map OVS_UNUSED,
+              struct ofpbuf *ofpacts,
               enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
     if (*arg == '\0') {
@@ -3387,7 +3481,9 @@ parse_DEC_TTL(char *arg, struct ofpbuf *ofpacts,
 }
 
 static void
-format_DEC_TTL(const struct ofpact_cnt_ids *a, struct ds *s)
+format_DEC_TTL(const struct ofpact_cnt_ids *a,
+               const struct ofputil_port_map *port_map OVS_UNUSED,
+               struct ds *s)
 {
     size_t i;
 
@@ -3428,7 +3524,9 @@ encode_SET_MPLS_LABEL(const struct ofpact_mpls_label *label,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_SET_MPLS_LABEL(char *arg, struct ofpbuf *ofpacts,
+parse_SET_MPLS_LABEL(char *arg,
+                     const struct ofputil_port_map *port_map OVS_UNUSED,
+                     struct ofpbuf *ofpacts,
                      enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
     struct ofpact_mpls_label *mpls_label = ofpact_put_SET_MPLS_LABEL(ofpacts);
@@ -3441,7 +3539,9 @@ parse_SET_MPLS_LABEL(char *arg, struct ofpbuf *ofpacts,
 }
 
 static void
-format_SET_MPLS_LABEL(const struct ofpact_mpls_label *a, struct ds *s)
+format_SET_MPLS_LABEL(const struct ofpact_mpls_label *a,
+                      const struct ofputil_port_map *port_map OVS_UNUSED,
+                      struct ds *s)
 {
     ds_put_format(s, "%sset_mpls_label(%s%"PRIu32"%s)%s",
                   colors.paren, colors.end, ntohl(a->label),
@@ -3471,7 +3571,9 @@ encode_SET_MPLS_TC(const struct ofpact_mpls_tc *tc,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_SET_MPLS_TC(char *arg, struct ofpbuf *ofpacts,
+parse_SET_MPLS_TC(char *arg,
+                  const struct ofputil_port_map *port_map OVS_UNUSED,
+                  struct ofpbuf *ofpacts,
                   enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
     struct ofpact_mpls_tc *mpls_tc = ofpact_put_SET_MPLS_TC(ofpacts);
@@ -3485,7 +3587,9 @@ parse_SET_MPLS_TC(char *arg, struct ofpbuf *ofpacts,
 }
 
 static void
-format_SET_MPLS_TC(const struct ofpact_mpls_tc *a, struct ds *s)
+format_SET_MPLS_TC(const struct ofpact_mpls_tc *a,
+                   const struct ofputil_port_map *port_map OVS_UNUSED,
+                   struct ds *s)
 {
     ds_put_format(s, "%sset_mpls_ttl(%s%"PRIu8"%s)%s",
                   colors.paren, colors.end, a->tc,
@@ -3516,7 +3620,9 @@ encode_SET_MPLS_TTL(const struct ofpact_mpls_ttl *ttl,
  * Returns NULL if successful, otherwise a malloc()'d string describing the
  * error.  The caller is responsible for freeing the returned string. */
 static char * OVS_WARN_UNUSED_RESULT
-parse_SET_MPLS_TTL(char *arg, struct ofpbuf *ofpacts,
+parse_SET_MPLS_TTL(char *arg,
+                   const struct ofputil_port_map *port_map OVS_UNUSED,
+                   struct ofpbuf *ofpacts,
                    enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
     struct ofpact_mpls_ttl *mpls_ttl = ofpact_put_SET_MPLS_TTL(ofpacts);
@@ -3530,7 +3636,9 @@ parse_SET_MPLS_TTL(char *arg, struct ofpbuf *ofpacts,
 }
 
 static void
-format_SET_MPLS_TTL(const struct ofpact_mpls_ttl *a, struct ds *s)
+format_SET_MPLS_TTL(const struct ofpact_mpls_ttl *a,
+                    const struct ofputil_port_map *port_map OVS_UNUSED,
+                    struct ds *s)
 {
     ds_put_format(s, "%sset_mpls_ttl(%s%"PRIu8"%s)%s",
                   colors.paren, colors.end, a->ttl,
@@ -3554,7 +3662,9 @@ encode_DEC_MPLS_TTL(const struct ofpact_null *null OVS_UNUSED,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_DEC_MPLS_TTL(char *arg OVS_UNUSED, struct ofpbuf *ofpacts,
+parse_DEC_MPLS_TTL(char *arg OVS_UNUSED,
+                   const struct ofputil_port_map *port_map OVS_UNUSED,
+                   struct ofpbuf *ofpacts,
                    enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
     ofpact_put_DEC_MPLS_TTL(ofpacts);
@@ -3562,7 +3672,9 @@ parse_DEC_MPLS_TTL(char *arg OVS_UNUSED, struct ofpbuf *ofpacts,
 }
 
 static void
-format_DEC_MPLS_TTL(const struct ofpact_null *a OVS_UNUSED, struct ds *s)
+format_DEC_MPLS_TTL(const struct ofpact_null *a OVS_UNUSED,
+                    const struct ofputil_port_map *port_map OVS_UNUSED,
+                    struct ds *s)
 {
     ds_put_format(s, "%sdec_mpls_ttl%s", colors.value, colors.end);
 }
@@ -3593,7 +3705,9 @@ encode_PUSH_MPLS(const struct ofpact_push_mpls *push_mpls,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_PUSH_MPLS(char *arg, struct ofpbuf *ofpacts,
+parse_PUSH_MPLS(char *arg,
+                const struct ofputil_port_map *port_map OVS_UNUSED,
+                struct ofpbuf *ofpacts,
                 enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
     uint16_t ethertype;
@@ -3607,7 +3721,9 @@ parse_PUSH_MPLS(char *arg, struct ofpbuf *ofpacts,
 }
 
 static void
-format_PUSH_MPLS(const struct ofpact_push_mpls *a, struct ds *s)
+format_PUSH_MPLS(const struct ofpact_push_mpls *a,
+                 const struct ofputil_port_map *port_map OVS_UNUSED,
+                 struct ds *s)
 {
     ds_put_format(s, "%spush_mpls:%s0x%04"PRIx16,
                   colors.param, colors.end, ntohs(a->ethertype));
@@ -3632,8 +3748,10 @@ encode_POP_MPLS(const struct ofpact_pop_mpls *pop_mpls,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_POP_MPLS(char *arg, struct ofpbuf *ofpacts,
-                    enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_POP_MPLS(char *arg,
+               const struct ofputil_port_map *port_map OVS_UNUSED,
+               struct ofpbuf *ofpacts,
+               enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
     uint16_t ethertype;
     char *error;
@@ -3646,7 +3764,9 @@ parse_POP_MPLS(char *arg, struct ofpbuf *ofpacts,
 }
 
 static void
-format_POP_MPLS(const struct ofpact_pop_mpls *a, struct ds *s)
+format_POP_MPLS(const struct ofpact_pop_mpls *a,
+                const struct ofputil_port_map *port_map OVS_UNUSED,
+                struct ds *s)
 {
     ds_put_format(s, "%spop_mpls:%s0x%04"PRIx16,
                   colors.param, colors.end, ntohs(a->ethertype));
@@ -3706,14 +3826,18 @@ parse_set_tunnel(char *arg, struct ofpbuf *ofpacts,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_SET_TUNNEL(char *arg, struct ofpbuf *ofpacts,
+parse_SET_TUNNEL(char *arg,
+                 const struct ofputil_port_map *port_map OVS_UNUSED,
+                 struct ofpbuf *ofpacts,
                  enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
     return parse_set_tunnel(arg, ofpacts, NXAST_RAW_SET_TUNNEL);
 }
 
 static void
-format_SET_TUNNEL(const struct ofpact_tunnel *a, struct ds *s)
+format_SET_TUNNEL(const struct ofpact_tunnel *a,
+                  const struct ofputil_port_map *port_map OVS_UNUSED,
+                  struct ds *s)
 {
     ds_put_format(s, "%sset_tunnel%s:%s%#"PRIx64, colors.param,
                   (a->tun_id > UINT32_MAX
@@ -3740,14 +3864,18 @@ encode_SET_QUEUE(const struct ofpact_queue *queue,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_SET_QUEUE(char *arg, struct ofpbuf *ofpacts,
+parse_SET_QUEUE(char *arg,
+                const struct ofputil_port_map *port_map OVS_UNUSED,
+                struct ofpbuf *ofpacts,
                 enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
     return str_to_u32(arg, &ofpact_put_SET_QUEUE(ofpacts)->queue_id);
 }
 
 static void
-format_SET_QUEUE(const struct ofpact_queue *a, struct ds *s)
+format_SET_QUEUE(const struct ofpact_queue *a,
+                 const struct ofputil_port_map *port_map OVS_UNUSED,
+                 struct ds *s)
 {
     ds_put_format(s, "%sset_queue:%s%"PRIu32,
                   colors.param, colors.end, a->queue_id);
@@ -3770,7 +3898,9 @@ encode_POP_QUEUE(const struct ofpact_null *null OVS_UNUSED,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_POP_QUEUE(const char *arg OVS_UNUSED, struct ofpbuf *ofpacts,
+parse_POP_QUEUE(const char *arg OVS_UNUSED,
+                const struct ofputil_port_map *port_map OVS_UNUSED,
+                struct ofpbuf *ofpacts,
                 enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
     ofpact_put_POP_QUEUE(ofpacts);
@@ -3778,7 +3908,9 @@ parse_POP_QUEUE(const char *arg OVS_UNUSED, struct ofpbuf *ofpacts,
 }
 
 static void
-format_POP_QUEUE(const struct ofpact_null *a OVS_UNUSED, struct ds *s)
+format_POP_QUEUE(const struct ofpact_null *a OVS_UNUSED,
+                 const struct ofputil_port_map *port_map OVS_UNUSED,
+                 struct ds *s)
 {
     ds_put_format(s, "%spop_queue%s", colors.value, colors.end);
 }
@@ -3841,7 +3973,9 @@ encode_FIN_TIMEOUT(const struct ofpact_fin_timeout *fin_timeout,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_FIN_TIMEOUT(char *arg, struct ofpbuf *ofpacts,
+parse_FIN_TIMEOUT(char *arg,
+                  const struct ofputil_port_map *port_map OVS_UNUSED,
+                  struct ofpbuf *ofpacts,
                   enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
     struct ofpact_fin_timeout *oft = ofpact_put_FIN_TIMEOUT(ofpacts);
@@ -3867,7 +4001,9 @@ parse_FIN_TIMEOUT(char *arg, struct ofpbuf *ofpacts,
 }
 
 static void
-format_FIN_TIMEOUT(const struct ofpact_fin_timeout *a, struct ds *s)
+format_FIN_TIMEOUT(const struct ofpact_fin_timeout *a,
+                   const struct ofputil_port_map *port_map OVS_UNUSED,
+                   struct ds *s)
 {
     ds_put_format(s, "%sfin_timeout(%s", colors.paren, colors.end);
     if (a->fin_idle_timeout) {
@@ -4025,7 +4161,9 @@ encode_RESUBMIT(const struct ofpact_resubmit *resubmit,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_RESUBMIT(char *arg, struct ofpbuf *ofpacts,
+parse_RESUBMIT(char *arg,
+               const struct ofputil_port_map *port_map,
+               struct ofpbuf *ofpacts,
                enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
     struct ofpact_resubmit *resubmit;
@@ -4035,7 +4173,8 @@ parse_RESUBMIT(char *arg, struct ofpbuf *ofpacts,
 
     in_port_s = strsep(&arg, ",");
     if (in_port_s && in_port_s[0]) {
-        if (!ofputil_port_from_string(in_port_s, &resubmit->in_port)) {
+        if (!ofputil_port_from_string(in_port_s, port_map,
+                                      &resubmit->in_port)) {
             return xasprintf("%s: resubmit to unknown port", in_port_s);
         }
     } else {
@@ -4074,15 +4213,16 @@ parse_RESUBMIT(char *arg, struct ofpbuf *ofpacts,
 }
 
 static void
-format_RESUBMIT(const struct ofpact_resubmit *a, struct ds *s)
+format_RESUBMIT(const struct ofpact_resubmit *a,
+                const struct ofputil_port_map *port_map, struct ds *s)
 {
     if (a->in_port != OFPP_IN_PORT && a->table_id == 255) {
         ds_put_format(s, "%sresubmit:%s", colors.special, colors.end);
-        ofputil_format_port(a->in_port, s);
+        ofputil_format_port(a->in_port, port_map, s);
     } else {
         ds_put_format(s, "%sresubmit(%s", colors.paren, colors.end);
         if (a->in_port != OFPP_IN_PORT) {
-            ofputil_format_port(a->in_port, s);
+            ofputil_format_port(a->in_port, port_map, s);
         }
         ds_put_char(s, ',');
         if (a->table_id != 255) {
@@ -4665,16 +4805,18 @@ encode_LEARN(const struct ofpact_learn *learn,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_LEARN(char *arg, struct ofpbuf *ofpacts,
+parse_LEARN(char *arg, const struct ofputil_port_map *port_map,
+            struct ofpbuf *ofpacts,
             enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
-    return learn_parse(arg, ofpacts);
+    return learn_parse(arg, port_map, ofpacts);
 }
 
 static void
-format_LEARN(const struct ofpact_learn *a, struct ds *s)
+format_LEARN(const struct ofpact_learn *a,
+             const struct ofputil_port_map *port_map, struct ds *s)
 {
-    learn_format(a, s);
+    learn_format(a, port_map, s);
 }
 
 /* Action structure for NXAST_CONJUNCTION. */
@@ -4726,7 +4868,9 @@ encode_CONJUNCTION(const struct ofpact_conjunction *oc,
 }
 
 static void
-format_CONJUNCTION(const struct ofpact_conjunction *oc, struct ds *s)
+format_CONJUNCTION(const struct ofpact_conjunction *oc,
+                   const struct ofputil_port_map *port_map OVS_UNUSED,
+                   struct ds *s)
 {
     ds_put_format(s, "%sconjunction(%s%"PRIu32",%d/%"PRIu8"%s)%s",
                   colors.paren, colors.end,
@@ -4735,7 +4879,9 @@ format_CONJUNCTION(const struct ofpact_conjunction *oc, struct ds *s)
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_CONJUNCTION(const char *arg, struct ofpbuf *ofpacts,
+parse_CONJUNCTION(const char *arg,
+                  const struct ofputil_port_map *port_map OVS_UNUSED,
+                  struct ofpbuf *ofpacts,
                   enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
     uint8_t n_clauses;
@@ -4878,14 +5024,18 @@ encode_MULTIPATH(const struct ofpact_multipath *mp,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_MULTIPATH(const char *arg, struct ofpbuf *ofpacts,
+parse_MULTIPATH(const char *arg,
+                const struct ofputil_port_map *port_map OVS_UNUSED,
+                struct ofpbuf *ofpacts,
                 enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
     return multipath_parse(ofpact_put_MULTIPATH(ofpacts), arg);
 }
 
 static void
-format_MULTIPATH(const struct ofpact_multipath *a, struct ds *s)
+format_MULTIPATH(const struct ofpact_multipath *a,
+                 const struct ofputil_port_map *port_map OVS_UNUSED,
+                 struct ds *s)
 {
     multipath_format(a, s);
 }
@@ -4941,7 +5091,9 @@ encode_NOTE(const struct ofpact_note *note,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_NOTE(const char *arg, struct ofpbuf *ofpacts,
+parse_NOTE(const char *arg,
+           const struct ofputil_port_map *port_map OVS_UNUSED,
+           struct ofpbuf *ofpacts,
            enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
     size_t start_ofs = ofpacts->size;
@@ -4958,7 +5110,8 @@ parse_NOTE(const char *arg, struct ofpbuf *ofpacts,
 }
 
 static void
-format_NOTE(const struct ofpact_note *a, struct ds *s)
+format_NOTE(const struct ofpact_note *a,
+            const struct ofputil_port_map *port_map OVS_UNUSED, struct ds *s)
 {
     ds_put_format(s, "%snote:%s", colors.param, colors.end);
     format_hex_arg(s, a->data, a->length);
@@ -4981,7 +5134,9 @@ encode_EXIT(const struct ofpact_null *null OVS_UNUSED,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_EXIT(char *arg OVS_UNUSED, struct ofpbuf *ofpacts,
+parse_EXIT(char *arg OVS_UNUSED,
+           const struct ofputil_port_map *port_map OVS_UNUSED,
+           struct ofpbuf *ofpacts,
            enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
     ofpact_put_EXIT(ofpacts);
@@ -4989,7 +5144,8 @@ parse_EXIT(char *arg OVS_UNUSED, struct ofpbuf *ofpacts,
 }
 
 static void
-format_EXIT(const struct ofpact_null *a OVS_UNUSED, struct ds *s)
+format_EXIT(const struct ofpact_null *a OVS_UNUSED,
+            const struct ofputil_port_map *port_map OVS_UNUSED, struct ds *s)
 {
     ds_put_format(s, "%sexit%s", colors.special, colors.end);
 }
@@ -5005,7 +5161,9 @@ encode_UNROLL_XLATE(const struct ofpact_unroll_xlate *unroll OVS_UNUSED,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_UNROLL_XLATE(char *arg OVS_UNUSED, struct ofpbuf *ofpacts OVS_UNUSED,
+parse_UNROLL_XLATE(char *arg OVS_UNUSED,
+                   const struct ofputil_port_map *port_map OVS_UNUSED,
+                   struct ofpbuf *ofpacts OVS_UNUSED,
                    enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
     OVS_NOT_REACHED();
@@ -5013,7 +5171,9 @@ parse_UNROLL_XLATE(char *arg OVS_UNUSED, struct ofpbuf *ofpacts OVS_UNUSED,
 }
 
 static void
-format_UNROLL_XLATE(const struct ofpact_unroll_xlate *a, struct ds *s)
+format_UNROLL_XLATE(const struct ofpact_unroll_xlate *a,
+                    const struct ofputil_port_map *port_map OVS_UNUSED,
+                    struct ds *s)
 {
     ds_put_format(s, "%sunroll_xlate(%s%stable=%s%"PRIu8
                   ", %scookie=%s%"PRIu64"%s)%s",
@@ -5070,15 +5230,17 @@ encode_CLONE(const struct ofpact_nest *clone,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_CLONE(char *arg, struct ofpbuf *ofpacts,
-             enum ofputil_protocol *usable_protocols)
+parse_CLONE(char *arg, const struct ofputil_port_map *port_map,
+            struct ofpbuf *ofpacts,
+            enum ofputil_protocol *usable_protocols)
 {
     const size_t clone_offset = ofpacts_pull(ofpacts);
     struct ofpact_nest *clone = ofpact_put_CLONE(ofpacts);
     char *error;
 
     ofpbuf_pull(ofpacts, sizeof *clone);
-    error = ofpacts_parse_copy(arg, ofpacts, usable_protocols, false, 0);
+    error = ofpacts_parse_copy(arg, port_map, ofpacts,
+                               usable_protocols, false, 0);
     /* header points to the action list */
     ofpacts->header = ofpbuf_push_uninit(ofpacts, sizeof *clone);
     clone = ofpacts->header;
@@ -5089,10 +5251,11 @@ parse_CLONE(char *arg, struct ofpbuf *ofpacts,
 }
 
 static void
-format_CLONE(const struct ofpact_nest *a, struct ds *s)
+format_CLONE(const struct ofpact_nest *a,
+             const struct ofputil_port_map *port_map, struct ds *s)
 {
     ds_put_format(s, "%sclone(%s", colors.paren, colors.end);
-    ofpacts_format(a->actions, ofpact_nest_get_action_len(a), s);
+    ofpacts_format(a->actions, ofpact_nest_get_action_len(a), port_map, s);
     ds_put_format(s, "%s)%s", colors.paren, colors.end);
 }
 
@@ -5251,7 +5414,8 @@ encode_SAMPLE(const struct ofpact_sample *sample,
  * Returns NULL if successful, otherwise a malloc()'d string describing the
  * error.  The caller is responsible for freeing the returned string. */
 static char * OVS_WARN_UNUSED_RESULT
-parse_SAMPLE(char *arg, struct ofpbuf *ofpacts,
+parse_SAMPLE(char *arg, const struct ofputil_port_map *port_map,
+             struct ofpbuf *ofpacts,
              enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
     struct ofpact_sample *os = ofpact_put_SAMPLE(ofpacts);
@@ -5274,7 +5438,8 @@ parse_SAMPLE(char *arg, struct ofpbuf *ofpacts,
         } else if (!strcmp(key, "obs_point_id")) {
             error = str_to_u32(value, &os->obs_point_id);
         } else if (!strcmp(key, "sampling_port")) {
-            if (!ofputil_port_from_string(value, &os->sampling_port)) {
+            if (!ofputil_port_from_string(value, port_map,
+                                          &os->sampling_port)) {
                 error = xasprintf("%s: unknown port", value);
             }
         } else if (!strcmp(key, "ingress")) {
@@ -5297,7 +5462,8 @@ parse_SAMPLE(char *arg, struct ofpbuf *ofpacts,
 }
 
 static void
-format_SAMPLE(const struct ofpact_sample *a, struct ds *s)
+format_SAMPLE(const struct ofpact_sample *a,
+              const struct ofputil_port_map *port_map, struct ds *s)
 {
     ds_put_format(s, "%ssample(%s%sprobability=%s%"PRIu16
                   ",%scollector_set_id=%s%"PRIu32
@@ -5309,8 +5475,8 @@ format_SAMPLE(const struct ofpact_sample *a, struct ds *s)
                   colors.param, colors.end, a->obs_domain_id,
                   colors.param, colors.end, a->obs_point_id);
     if (a->sampling_port != OFPP_NONE) {
-        ds_put_format(s, ",%ssampling_port=%s%"PRIu32,
-                      colors.param, colors.end, a->sampling_port);
+        ds_put_format(s, ",%ssampling_port=%s", colors.param, colors.end);
+        ofputil_format_port(a->sampling_port, port_map, s);
     }
     if (a->direction == NX_ACTION_SAMPLE_INGRESS) {
         ds_put_format(s, ",%singress%s", colors.param, colors.end);
@@ -5350,7 +5516,9 @@ encode_DEBUG_RECIRC(const struct ofpact_null *n OVS_UNUSED,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_DEBUG_RECIRC(char *arg OVS_UNUSED, struct ofpbuf *ofpacts,
+parse_DEBUG_RECIRC(char *arg OVS_UNUSED,
+                   const struct ofputil_port_map *port_map OVS_UNUSED,
+                   struct ofpbuf *ofpacts,
                    enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
     ofpact_put_DEBUG_RECIRC(ofpacts);
@@ -5358,7 +5526,9 @@ parse_DEBUG_RECIRC(char *arg OVS_UNUSED, struct ofpbuf *ofpacts,
 }
 
 static void
-format_DEBUG_RECIRC(const struct ofpact_null *a OVS_UNUSED, struct ds *s)
+format_DEBUG_RECIRC(const struct ofpact_null *a OVS_UNUSED,
+                    const struct ofputil_port_map *port_map OVS_UNUSED,
+                    struct ds *s)
 {
     ds_put_format(s, "%sdebug_recirc%s", colors.value, colors.end);
 }
@@ -5623,8 +5793,9 @@ encode_CT(const struct ofpact_conntrack *conntrack,
     nac->len = htons(len);
 }
 
-static char * OVS_WARN_UNUSED_RESULT parse_NAT(char *arg, struct ofpbuf *,
-                                               enum ofputil_protocol * OVS_UNUSED);
+static char *OVS_WARN_UNUSED_RESULT
+parse_NAT(char *arg, const struct ofputil_port_map *port_map OVS_UNUSED,
+          struct ofpbuf *, enum ofputil_protocol * OVS_UNUSED);
 
 /* Parses 'arg' as the argument to a "ct" action, and appends such an
  * action to 'ofpacts'.
@@ -5632,7 +5803,8 @@ static char * OVS_WARN_UNUSED_RESULT parse_NAT(char *arg, struct ofpbuf *,
  * Returns NULL if successful, otherwise a malloc()'d string describing the
  * error.  The caller is responsible for freeing the returned string. */
 static char * OVS_WARN_UNUSED_RESULT
-parse_CT(char *arg, struct ofpbuf *ofpacts,
+parse_CT(char *arg, const struct ofputil_port_map *port_map,
+         struct ofpbuf *ofpacts,
          enum ofputil_protocol *usable_protocols)
 {
     const size_t ct_offset = ofpacts_pull(ofpacts);
@@ -5668,7 +5840,7 @@ parse_CT(char *arg, struct ofpbuf *ofpacts,
         } else if (!strcmp(key, "nat")) {
             const size_t nat_offset = ofpacts_pull(ofpacts);
 
-            error = parse_NAT(value, ofpacts, usable_protocols);
+            error = parse_NAT(value, port_map, ofpacts, usable_protocols);
             /* Update CT action pointer and length. */
             ofpacts->header = ofpbuf_push_uninit(ofpacts, nat_offset);
             oc = ofpacts->header;
@@ -5681,8 +5853,8 @@ parse_CT(char *arg, struct ofpbuf *ofpacts,
             /* Initializes 'usable_protocol2', fold it back to
              * '*usable_protocols' afterwards, so that we do not lose
              * restrictions already in there. */
-            error = ofpacts_parse_copy(value, ofpacts, &usable_protocols2,
-                                       false, OFPACT_CT);
+            error = ofpacts_parse_copy(value, port_map, ofpacts,
+                                       &usable_protocols2, false, OFPACT_CT);
             *usable_protocols &= usable_protocols2;
             ofpacts->header = ofpbuf_push_uninit(ofpacts, exec_offset);
             oc = ofpacts->header;
@@ -5720,10 +5892,14 @@ format_alg(int port, struct ds *s)
     }
 }
 
-static void format_NAT(const struct ofpact_nat *a, struct ds *ds);
+static void format_NAT(const struct ofpact_nat *,
+                       const struct ofputil_port_map *port_map,
+                       struct ds *ds);
 
 static void
-format_CT(const struct ofpact_conntrack *a, struct ds *s)
+format_CT(const struct ofpact_conntrack *a,
+          const struct ofputil_port_map *port_map,
+          struct ds *s)
 {
     ds_put_format(s, "%sct(%s", colors.paren, colors.end);
     if (a->flags & NX_CT_F_COMMIT) {
@@ -5749,14 +5925,14 @@ format_CT(const struct ofpact_conntrack *a, struct ds *s)
     const struct ofpact *action = a->actions;
     size_t actions_len = ofpact_ct_get_action_len(a);
     if (actions_len && action->type == OFPACT_NAT) {
-        format_NAT(ofpact_get_NAT(action), s);
+        format_NAT(ofpact_get_NAT(action), port_map, s);
         ds_put_char(s, ',');
         actions_len -= OFPACT_ALIGN(action->len);
         action = ofpact_next(action);
     }
     if (actions_len) {
         ds_put_format(s, "%sexec(%s", colors.paren, colors.end);
-        ofpacts_format(action, actions_len, s);
+        ofpacts_format(action, actions_len, port_map, s);
         ds_put_format(s, "%s),%s", colors.paren, colors.end);
     }
     format_alg(a->alg, s);
@@ -5782,7 +5958,9 @@ encode_CT_CLEAR(const struct ofpact_null *null OVS_UNUSED,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_CT_CLEAR(char *arg OVS_UNUSED, struct ofpbuf *ofpacts,
+parse_CT_CLEAR(char *arg OVS_UNUSED,
+               const struct ofputil_port_map *port_map OVS_UNUSED,
+               struct ofpbuf *ofpacts,
                enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
     ofpact_put_CT_CLEAR(ofpacts);
@@ -5790,10 +5968,13 @@ parse_CT_CLEAR(char *arg OVS_UNUSED, struct ofpbuf *ofpacts,
 }
 
 static void
-format_CT_CLEAR(const struct ofpact_null *a OVS_UNUSED, struct ds *s)
+format_CT_CLEAR(const struct ofpact_null *a OVS_UNUSED,
+                const struct ofputil_port_map *port_map OVS_UNUSED,
+                struct ds *s)
 {
     ds_put_format(s, "%sct_clear%s", colors.value, colors.end);
-}
+}
+
 /* NAT action. */
 
 /* Which optional fields are present? */
@@ -5973,7 +6154,9 @@ decode_NXAST_RAW_NAT(const struct nx_action_nat *nan,
 }
 
 static void
-format_NAT(const struct ofpact_nat *a, struct ds *ds)
+format_NAT(const struct ofpact_nat *a,
+           const struct ofputil_port_map *port_map OVS_UNUSED,
+           struct ds *ds)
 {
     ds_put_format(ds, "%snat%s", colors.paren, colors.end);
 
@@ -6097,7 +6280,9 @@ error:
  * Returns NULL if successful, otherwise a malloc()'d string describing the
  * error.  The caller is responsible for freeing the returned string. */
 static char * OVS_WARN_UNUSED_RESULT
-parse_NAT(char *arg, struct ofpbuf *ofpacts,
+parse_NAT(char *arg,
+          const struct ofputil_port_map *port_map OVS_UNUSED,
+          struct ofpbuf *ofpacts,
           enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
     struct ofpact_nat *on = ofpact_put_NAT(ofpacts);
@@ -6187,8 +6372,10 @@ encode_OUTPUT_TRUNC(const struct ofpact_output_trunc *output_trunc,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_OUTPUT_TRUNC(const char *arg, struct ofpbuf *ofpacts OVS_UNUSED,
-                 enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_OUTPUT_TRUNC(const char *arg,
+                   const struct ofputil_port_map *port_map OVS_UNUSED,
+                   struct ofpbuf *ofpacts OVS_UNUSED,
+                   enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
     /* Disable output_trunc parsing.  Expose as output(port=N,max_len=M) and
      * reuse parse_OUTPUT to parse output_trunc action. */
@@ -6196,10 +6383,12 @@ parse_OUTPUT_TRUNC(const char *arg, struct ofpbuf *ofpacts OVS_UNUSED,
 }
 
 static void
-format_OUTPUT_TRUNC(const struct ofpact_output_trunc *a, struct ds *s)
+format_OUTPUT_TRUNC(const struct ofpact_output_trunc *a,
+                    const struct ofputil_port_map *port_map, struct ds *s)
 {
-     ds_put_format(s, "%soutput%s(port=%"PRIu32",max_len=%"PRIu32")",
-                   colors.special, colors.end, a->port, a->max_len);
+    ds_put_format(s, "%soutput%s(port=", colors.special, colors.end);
+    ofputil_format_port(a->port, port_map, s);
+    ds_put_format(s, ",max_len=%"PRIu32")", a->max_len);
 }
 
 
@@ -6215,7 +6404,9 @@ encode_METER(const struct ofpact_meter *meter,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_METER(char *arg, struct ofpbuf *ofpacts,
+parse_METER(char *arg,
+            const struct ofputil_port_map *port_map OVS_UNUSED,
+            struct ofpbuf *ofpacts,
             enum ofputil_protocol *usable_protocols)
 {
     *usable_protocols &= OFPUTIL_P_OF13_UP;
@@ -6223,7 +6414,9 @@ parse_METER(char *arg, struct ofpbuf *ofpacts,
 }
 
 static void
-format_METER(const struct ofpact_meter *a, struct ds *s)
+format_METER(const struct ofpact_meter *a,
+             const struct ofputil_port_map *port_map OVS_UNUSED,
+             struct ds *s)
 {
     ds_put_format(s, "%smeter:%s%"PRIu32,
                   colors.param, colors.end, a->meter_id);
@@ -6242,7 +6435,9 @@ encode_CLEAR_ACTIONS(const struct ofpact_null *null OVS_UNUSED,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_CLEAR_ACTIONS(char *arg OVS_UNUSED, struct ofpbuf *ofpacts,
+parse_CLEAR_ACTIONS(char *arg OVS_UNUSED,
+                    const struct ofputil_port_map *port_map OVS_UNUSED,
+                    struct ofpbuf *ofpacts,
                     enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
     ofpact_put_CLEAR_ACTIONS(ofpacts);
@@ -6250,7 +6445,9 @@ parse_CLEAR_ACTIONS(char *arg OVS_UNUSED, struct ofpbuf *ofpacts,
 }
 
 static void
-format_CLEAR_ACTIONS(const struct ofpact_null *a OVS_UNUSED, struct ds *s)
+format_CLEAR_ACTIONS(const struct ofpact_null *a OVS_UNUSED,
+                     const struct ofputil_port_map *port_map OVS_UNUSED,
+                     struct ds *s)
 {
     ds_put_format(s, "%sclear_actions%s", colors.value, colors.end);
 }
@@ -6273,7 +6470,8 @@ encode_WRITE_ACTIONS(const struct ofpact_nest *actions,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_WRITE_ACTIONS(char *arg, struct ofpbuf *ofpacts,
+parse_WRITE_ACTIONS(char *arg, const struct ofputil_port_map *port_map,
+                    struct ofpbuf *ofpacts,
                     enum ofputil_protocol *usable_protocols)
 {
     size_t ofs = ofpacts_pull(ofpacts);
@@ -6291,7 +6489,7 @@ parse_WRITE_ACTIONS(char *arg, struct ofpbuf *ofpacts,
      * that it doesn't actually include the nested actions.  That means that
      * ofpacts_parse() would reject them as being part of an Apply-Actions that
      * follows a Write-Actions, which is an invalid order.  */
-    error = ofpacts_parse(arg, ofpacts, usable_protocols, false,
+    error = ofpacts_parse(arg, port_map, ofpacts, usable_protocols, false,
                           OFPACT_WRITE_ACTIONS);
 
     /* Put the Write-Actions back on and update its length. */
@@ -6305,10 +6503,11 @@ parse_WRITE_ACTIONS(char *arg, struct ofpbuf *ofpacts,
 }
 
 static void
-format_WRITE_ACTIONS(const struct ofpact_nest *a, struct ds *s)
+format_WRITE_ACTIONS(const struct ofpact_nest *a,
+                     const struct ofputil_port_map *port_map, struct ds *s)
 {
     ds_put_format(s, "%swrite_actions(%s", colors.paren, colors.end);
-    ofpacts_format(a->actions, ofpact_nest_get_action_len(a), s);
+    ofpacts_format(a->actions, ofpact_nest_get_action_len(a), port_map, s);
     ds_put_format(s, "%s)%s", colors.paren, colors.end);
 }
 
@@ -6364,7 +6563,9 @@ encode_WRITE_METADATA(const struct ofpact_metadata *metadata,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_WRITE_METADATA(char *arg, struct ofpbuf *ofpacts,
+parse_WRITE_METADATA(char *arg,
+                     const struct ofputil_port_map *port_map OVS_UNUSED,
+                     struct ofpbuf *ofpacts,
                      enum ofputil_protocol *usable_protocols)
 {
     struct ofpact_metadata *om;
@@ -6389,7 +6590,9 @@ parse_WRITE_METADATA(char *arg, struct ofpbuf *ofpacts,
 }
 
 static void
-format_WRITE_METADATA(const struct ofpact_metadata *a, struct ds *s)
+format_WRITE_METADATA(const struct ofpact_metadata *a,
+                      const struct ofputil_port_map *port_map OVS_UNUSED,
+                      struct ds *s)
 {
     ds_put_format(s, "%swrite_metadata:%s%#"PRIx64,
                   colors.param, colors.end, ntohll(a->metadata));
@@ -6420,7 +6623,9 @@ encode_GOTO_TABLE(const struct ofpact_goto_table *goto_table,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_GOTO_TABLE(char *arg, struct ofpbuf *ofpacts,
+parse_GOTO_TABLE(char *arg,
+                 const struct ofputil_port_map *port_map OVS_UNUSED,
+                 struct ofpbuf *ofpacts,
                  enum ofputil_protocol *usable_protocols OVS_UNUSED)
 {
     struct ofpact_goto_table *ogt = ofpact_put_GOTO_TABLE(ofpacts);
@@ -6432,7 +6637,9 @@ parse_GOTO_TABLE(char *arg, struct ofpbuf *ofpacts,
 }
 
 static void
-format_GOTO_TABLE(const struct ofpact_goto_table *a, struct ds *s)
+format_GOTO_TABLE(const struct ofpact_goto_table *a,
+                  const struct ofputil_port_map *port_map OVS_UNUSED,
+                  struct ds *s)
 {
     ds_put_format(s, "%sgoto_table:%s%"PRIu8,
                   colors.param, colors.end, a->table_id);
@@ -8139,12 +8346,14 @@ ofpacts_get_meter(const struct ofpact ofpacts[], size_t ofpacts_len)
 /* Formatting ofpacts. */
 
 static void
-ofpact_format(const struct ofpact *a, struct ds *s)
+ofpact_format(const struct ofpact *a,
+              const struct ofputil_port_map *port_map, struct ds *s)
 {
     switch (a->type) {
 #define OFPACT(ENUM, STRUCT, MEMBER, NAME)                              \
         case OFPACT_##ENUM:                                             \
-            format_##ENUM(ALIGNED_CAST(const struct STRUCT *, a), s);   \
+            format_##ENUM(ALIGNED_CAST(const struct STRUCT *, a),       \
+                          port_map, s);                               \
             break;
         OFPACTS
 #undef OFPACT
@@ -8154,10 +8363,11 @@ ofpact_format(const struct ofpact *a, struct ds *s)
 }
 
 /* Appends a string representing the 'ofpacts_len' bytes of ofpacts in
- * 'ofpacts' to 'string'. */
+ * 'ofpacts' to 'string'.  If 'port_map' is nonnull, uses it to translate
+ * port numbers to names in output. */
 void
 ofpacts_format(const struct ofpact *ofpacts, size_t ofpacts_len,
-               struct ds *string)
+               const struct ofputil_port_map *port_map, struct ds *string)
 {
     if (!ofpacts_len) {
         ds_put_format(string, "%sdrop%s", colors.drop, colors.end);
@@ -8169,7 +8379,7 @@ ofpacts_format(const struct ofpact *ofpacts, size_t ofpacts_len,
                 ds_put_char(string, ',');
             }
 
-            ofpact_format(a, string);
+            ofpact_format(a, port_map, string);
         }
     }
 }
@@ -8222,13 +8432,14 @@ ofpact_finish(struct ofpbuf *ofpacts, struct ofpact *ofpact)
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-ofpact_parse(enum ofpact_type type, char *value, struct ofpbuf *ofpacts,
+ofpact_parse(enum ofpact_type type, char *value,
+             const struct ofputil_port_map *port_map, struct ofpbuf *ofpacts,
              enum ofputil_protocol *usable_protocols)
 {
     switch (type) {
-#define OFPACT(ENUM, STRUCT, MEMBER, NAME)                            \
+#define OFPACT(ENUM, STRUCT, MEMBER, NAME)                              \
         case OFPACT_##ENUM:                                             \
-            return parse_##ENUM(value, ofpacts, usable_protocols);
+            return parse_##ENUM(value, port_map, ofpacts, usable_protocols);
         OFPACTS
 #undef OFPACT
     default:
@@ -8258,7 +8469,8 @@ ofpact_type_from_name(const char *name, enum ofpact_type *type)
  * If 'outer_action' is specified, indicates that the actions being parsed
  * are nested within another action of the type specified in 'outer_action'. */
 static char * OVS_WARN_UNUSED_RESULT
-ofpacts_parse__(char *str, struct ofpbuf *ofpacts,
+ofpacts_parse__(char *str, const struct ofputil_port_map *port_map,
+                struct ofpbuf *ofpacts,
                 enum ofputil_protocol *usable_protocols,
                 bool allow_instructions, enum ofpact_type outer_action)
 {
@@ -8276,14 +8488,16 @@ ofpacts_parse__(char *str, struct ofpbuf *ofpacts,
         ofp_port_t port;
 
         if (ofpact_type_from_name(key, &type)) {
-            error = ofpact_parse(type, value, ofpacts, usable_protocols);
+            error = ofpact_parse(type, value, port_map,
+                                 ofpacts, usable_protocols);
             inst = ovs_instruction_type_from_ofpact_type(type);
         } else if (!strcasecmp(key, "mod_vlan_vid")) {
             error = parse_set_vlan_vid(value, ofpacts, true);
         } else if (!strcasecmp(key, "mod_vlan_pcp")) {
             error = parse_set_vlan_pcp(value, ofpacts, true);
         } else if (!strcasecmp(key, "set_nw_ttl")) {
-            error = parse_SET_IP_TTL(value, ofpacts, usable_protocols);
+            error = parse_SET_IP_TTL(value, port_map,
+                                     ofpacts, usable_protocols);
         } else if (!strcasecmp(key, "pop_vlan")) {
             error = parse_pop_vlan(ofpacts);
         } else if (!strcasecmp(key, "set_tunnel64")) {
@@ -8292,12 +8506,12 @@ ofpacts_parse__(char *str, struct ofpbuf *ofpacts,
         } else if (!strcasecmp(key, "load")) {
             error = parse_reg_load(value, ofpacts);
         } else if (!strcasecmp(key, "bundle_load")) {
-            error = parse_bundle_load(value, ofpacts);
+            error = parse_bundle_load(value, port_map, ofpacts);
         } else if (!strcasecmp(key, "drop")) {
             drop = true;
         } else if (!strcasecmp(key, "apply_actions")) {
             return xstrdup("apply_actions is the default instruction");
-        } else if (ofputil_port_from_string(key, &port)) {
+        } else if (ofputil_port_from_string(key, port_map, &port)) {
             ofpact_put_OUTPUT(ofpacts)->port = port;
         } else {
             return xasprintf("unknown action %s", key);
@@ -8343,12 +8557,12 @@ ofpacts_parse__(char *str, struct ofpbuf *ofpacts,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-ofpacts_parse(char *str, struct ofpbuf *ofpacts,
-              enum ofputil_protocol *usable_protocols, bool allow_instructions,
-              enum ofpact_type outer_action)
+ofpacts_parse(char *str, const struct ofputil_port_map *port_map,
+              struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols,
+              bool allow_instructions, enum ofpact_type outer_action)
 {
     uint32_t orig_size = ofpacts->size;
-    char *error = ofpacts_parse__(str, ofpacts, usable_protocols,
+    char *error = ofpacts_parse__(str, port_map, ofpacts, usable_protocols,
                                   allow_instructions, outer_action);
     if (error) {
         ofpacts->size = orig_size;
@@ -8357,7 +8571,8 @@ ofpacts_parse(char *str, struct ofpbuf *ofpacts,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-ofpacts_parse_copy(const char *s_, struct ofpbuf *ofpacts,
+ofpacts_parse_copy(const char *s_, const struct ofputil_port_map *port_map,
+                   struct ofpbuf *ofpacts,
                    enum ofputil_protocol *usable_protocols,
                    bool allow_instructions, enum ofpact_type outer_action)
 {
@@ -8366,8 +8581,8 @@ ofpacts_parse_copy(const char *s_, struct ofpbuf *ofpacts,
     *usable_protocols = OFPUTIL_P_ANY;
 
     s = xstrdup(s_);
-    error = ofpacts_parse(s, ofpacts, usable_protocols, allow_instructions,
-                          outer_action);
+    error = ofpacts_parse(s, port_map, ofpacts, usable_protocols,
+                          allow_instructions, outer_action);
     free(s);
 
     return error;
@@ -8380,10 +8595,12 @@ ofpacts_parse_copy(const char *s_, struct ofpbuf *ofpacts,
  * 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
-ofpacts_parse_actions(const char *s, struct ofpbuf *ofpacts,
+ofpacts_parse_actions(const char *s, const struct ofputil_port_map *port_map,
+                      struct ofpbuf *ofpacts,
                       enum ofputil_protocol *usable_protocols)
 {
-    return ofpacts_parse_copy(s, ofpacts, usable_protocols, false, 0);
+    return ofpacts_parse_copy(s, port_map, ofpacts, usable_protocols,
+                              false, 0);
 }
 
 /* Parses 's' as a set of OpenFlow instructions and appends the instructions to
@@ -8392,10 +8609,12 @@ ofpacts_parse_actions(const char *s, struct ofpbuf *ofpacts,
  * 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
-ofpacts_parse_instructions(const char *s, struct ofpbuf *ofpacts,
+ofpacts_parse_instructions(const char *s,
+                           const struct ofputil_port_map *port_map,
+                           struct ofpbuf *ofpacts,
                            enum ofputil_protocol *usable_protocols)
 {
-    return ofpacts_parse_copy(s, ofpacts, usable_protocols, true, 0);
+    return ofpacts_parse_copy(s, port_map, ofpacts, usable_protocols, true, 0);
 }
 
 const char *
diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c
index c8cac5b4765c..09550dcc9d73 100644
--- a/lib/ofp-parse.c
+++ b/lib/ofp-parse.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2011, 2012, 2013, 2014, 2015, 2016 Nicira, Inc.
+ * Copyright (c) 2010, 2011, 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.
@@ -235,7 +235,8 @@ parse_protocol(const char *name, const struct protocol **p_out)
  * Returns NULL if successful, otherwise a malloc()'d string describing the
  * error.  The caller is responsible for freeing the returned string. */
 static char * OVS_WARN_UNUSED_RESULT
-parse_field(const struct mf_field *mf, const char *s, struct match *match,
+parse_field(const struct mf_field *mf, const char *s,
+            const struct ofputil_port_map *port_map, struct match *match,
             enum ofputil_protocol *usable_protocols)
 {
     union mf_value value, mask;
@@ -247,7 +248,7 @@ parse_field(const struct mf_field *mf, const char *s, struct match *match,
         s = "0/0";
     }
 
-    error = mf_parse(mf, s, &value, &mask);
+    error = mf_parse(mf, s, port_map, &value, &mask);
     if (!error) {
         *usable_protocols &= mf_set(mf, &value, &mask, match, &error);
     }
@@ -281,7 +282,7 @@ parse_subfield(const char *name, const char *str_value, struct match *match,
             struct ds ds;
 
             ds_init(&ds);
-            mf_format(sf.field, &val, NULL, &ds);
+            mf_format(sf.field, &val, NULL, NULL, &ds);
             error = xasprintf("%s: value %s does not fit into %d bits",
                               name, ds_cstr(&ds), sf.n_bits);
             ds_destroy(&ds);
@@ -316,6 +317,7 @@ extract_actions(char *s)
 
 static char * OVS_WARN_UNUSED_RESULT
 parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string,
+                const struct ofputil_port_map *port_map,
                 enum ofputil_protocol *usable_protocols)
 {
     enum {
@@ -431,7 +433,8 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string,
                    || !strcmp(name, "allow_hidden_fields")) {
              /* ignore these fields. */
         } else if ((mf = mf_from_name(name)) != NULL) {
-            error = parse_field(mf, value, &fm->match, usable_protocols);
+            error = parse_field(mf, value, port_map,
+                                &fm->match, usable_protocols);
         } else if (strchr(name, '[')) {
             error = parse_subfield(name, value, &fm->match, usable_protocols);
         } else {
@@ -445,7 +448,8 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string,
                     *usable_protocols &= OFPUTIL_P_TID;
                 }
             } else if (fields & F_OUT_PORT && !strcmp(name, "out_port")) {
-                if (!ofputil_port_from_string(value, &fm->out_port)) {
+                if (!ofputil_port_from_string(value, port_map,
+                                              &fm->out_port)) {
                     error = xasprintf("%s is not a valid OpenFlow port",
                                       value);
                 }
@@ -540,7 +544,7 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string,
         char *error;
 
         ofpbuf_init(&ofpacts, 32);
-        error = ofpacts_parse_instructions(act_str, &ofpacts,
+        error = ofpacts_parse_instructions(act_str, port_map, &ofpacts,
                                            &action_usable_protocols);
         *usable_protocols &= action_usable_protocols;
         if (!error) {
@@ -588,12 +592,13 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string,
  * error.  The caller is responsible for freeing the returned string. */
 char * OVS_WARN_UNUSED_RESULT
 parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
+              const struct ofputil_port_map *port_map,
               enum ofputil_protocol *usable_protocols)
 {
     char *string = xstrdup(str_);
     char *error;
 
-    error = parse_ofp_str__(fm, command, string, usable_protocols);
+    error = parse_ofp_str__(fm, command, string, port_map, usable_protocols);
     if (error) {
         fm->ofpacts = NULL;
         fm->ofpacts_len = 0;
@@ -607,6 +612,7 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
  * both 'po->ofpacts' and 'po->packet' must be free()d by the caller. */
 static char * OVS_WARN_UNUSED_RESULT
 parse_ofp_packet_out_str__(struct ofputil_packet_out *po, char *string,
+                           const struct ofputil_port_map *port_map,
                            enum ofputil_protocol *usable_protocols)
 {
     enum ofputil_protocol action_usable_protocols;
@@ -633,7 +639,7 @@ parse_ofp_packet_out_str__(struct ofputil_packet_out *po, char *string,
         }
 
         if (!strcmp(name, "in_port")) {
-            if (!ofputil_port_from_string(value, &po->in_port)) {
+            if (!ofputil_port_from_string(value, port_map, &po->in_port)) {
                 error = xasprintf("%s is not a valid OpenFlow port", value);
                 goto out;
             }
@@ -664,7 +670,7 @@ parse_ofp_packet_out_str__(struct ofputil_packet_out *po, char *string,
     }
 
     if (act_str) {
-        error = ofpacts_parse_actions(act_str, &ofpacts,
+        error = ofpacts_parse_actions(act_str, port_map, &ofpacts,
                                       &action_usable_protocols);
         *usable_protocols &= action_usable_protocols;
         if (error) {
@@ -690,12 +696,13 @@ out:
  * error.  The caller is responsible for freeing the returned string. */
 char * OVS_WARN_UNUSED_RESULT
 parse_ofp_packet_out_str(struct ofputil_packet_out *po, const char *str_,
+                         const struct ofputil_port_map *port_map,
                          enum ofputil_protocol *usable_protocols)
 {
     char *string = xstrdup(str_);
     char *error;
 
-    error = parse_ofp_packet_out_str__(po, string, usable_protocols);
+    error = parse_ofp_packet_out_str__(po, string, port_map, usable_protocols);
     if (error) {
         po->ofpacts = NULL;
         po->ofpacts_len = 0;
@@ -931,7 +938,9 @@ parse_ofp_meter_mod_str(struct ofputil_meter_mod *mm, const char *str_,
 
 static char * OVS_WARN_UNUSED_RESULT
 parse_flow_monitor_request__(struct ofputil_flow_monitor_request *fmr,
-                             const char *str_, char *string,
+                             const char *str_,
+                             const struct ofputil_port_map *port_map,
+                             char *string,
                              enum ofputil_protocol *usable_protocols)
 {
     static atomic_count id = ATOMIC_COUNT_INIT(0);
@@ -967,8 +976,8 @@ parse_flow_monitor_request__(struct ofputil_flow_monitor_request *fmr,
                 match_set_nw_proto(&fmr->match, p->nw_proto);
             }
         } else if (mf_from_name(name)) {
-            error = parse_field(mf_from_name(name), value, &fmr->match,
-                                usable_protocols);
+            error = parse_field(mf_from_name(name), value, port_map,
+                                &fmr->match, usable_protocols);
         } else {
             if (!*value) {
                 return xasprintf("%s: field %s missing value", str_, name);
@@ -998,10 +1007,11 @@ parse_flow_monitor_request__(struct ofputil_flow_monitor_request *fmr,
 char * OVS_WARN_UNUSED_RESULT
 parse_flow_monitor_request(struct ofputil_flow_monitor_request *fmr,
                            const char *str_,
+                           const struct ofputil_port_map *port_map,
                            enum ofputil_protocol *usable_protocols)
 {
     char *string = xstrdup(str_);
-    char *error = parse_flow_monitor_request__(fmr, str_, string,
+    char *error = parse_flow_monitor_request__(fmr, str_, port_map, string,
                                                usable_protocols);
     free(string);
     return error;
@@ -1018,10 +1028,11 @@ parse_flow_monitor_request(struct ofputil_flow_monitor_request *fmr,
  * error.  The caller is responsible for freeing the returned string. */
 char * OVS_WARN_UNUSED_RESULT
 parse_ofp_flow_mod_str(struct ofputil_flow_mod *fm, const char *string,
-                       int command,
+                       const struct ofputil_port_map *port_map, int command,
                        enum ofputil_protocol *usable_protocols)
 {
-    char *error = parse_ofp_str(fm, command, string, usable_protocols);
+    char *error = parse_ofp_str(fm, command, string, port_map,
+                                usable_protocols);
 
     if (!error) {
         /* Normalize a copy of the match.  This ensures that non-normalized
@@ -1172,7 +1183,8 @@ parse_ofp_table_mod(struct ofputil_table_mod *tm, const char *table_id,
  * 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_flow_mod_file(const char *file_name, int command,
+parse_ofp_flow_mod_file(const char *file_name,
+                        const struct ofputil_port_map *port_map, int command,
                         struct ofputil_flow_mod **fms, size_t *n_fms,
                         enum ofputil_protocol *usable_protocols)
 {
@@ -1202,8 +1214,8 @@ parse_ofp_flow_mod_file(const char *file_name, int command,
         if (*n_fms >= allocated_fms) {
             *fms = x2nrealloc(*fms, &allocated_fms, sizeof **fms);
         }
-        error = parse_ofp_flow_mod_str(&(*fms)[*n_fms], ds_cstr(&s), command,
-                                       &usable);
+        error = parse_ofp_flow_mod_str(&(*fms)[*n_fms], ds_cstr(&s), port_map,
+                                       command, &usable);
         if (error) {
             char *err_msg;
             size_t i;
@@ -1238,12 +1250,13 @@ parse_ofp_flow_mod_file(const char *file_name, int command,
 char * OVS_WARN_UNUSED_RESULT
 parse_ofp_flow_stats_request_str(struct ofputil_flow_stats_request *fsr,
                                  bool aggregate, const char *string,
+                                 const struct ofputil_port_map *port_map,
                                  enum ofputil_protocol *usable_protocols)
 {
     struct ofputil_flow_mod fm;
     char *error;
 
-    error = parse_ofp_str(&fm, -1, string, usable_protocols);
+    error = parse_ofp_str(&fm, -1, string, port_map, usable_protocols);
     if (error) {
         return error;
     }
@@ -1278,7 +1291,7 @@ parse_ofp_flow_stats_request_str(struct ofputil_flow_stats_request *fsr,
 char *
 parse_ofp_exact_flow(struct flow *flow, struct flow_wildcards *wc,
                      const struct tun_table *tun_table, const char *s,
-                     const struct simap *portno_names)
+                     const struct ofputil_port_map *port_map)
 {
     char *pos, *key, *value_s;
     char *error = NULL;
@@ -1336,28 +1349,17 @@ parse_ofp_exact_flow(struct flow *flow, struct flow_wildcards *wc,
                 goto exit;
             }
 
-            if (!strcmp(key, "in_port")
-                && portno_names
-                && simap_contains(portno_names, value_s)) {
-                flow->in_port.ofp_port = u16_to_ofp(
-                    simap_get(portno_names, value_s));
-                if (wc) {
-                    wc->masks.in_port.ofp_port
-                        = u16_to_ofp(ntohs(OVS_BE16_MAX));
-                }
-            } else {
-                field_error = mf_parse_value(mf, value_s, &value);
-                if (field_error) {
-                    error = xasprintf("%s: bad value for %s (%s)",
-                                      s, key, field_error);
-                    free(field_error);
-                    goto exit;
-                }
+            field_error = mf_parse_value(mf, value_s, port_map, &value);
+            if (field_error) {
+                error = xasprintf("%s: bad value for %s (%s)",
+                                  s, key, field_error);
+                free(field_error);
+                goto exit;
+            }
 
-                mf_set_flow_value(mf, &value, flow);
-                if (wc) {
-                    mf_mask_field(mf, wc);
-                }
+            mf_set_flow_value(mf, &value, flow);
+            if (wc) {
+                mf_mask_field(mf, wc);
             }
         }
     }
@@ -1379,8 +1381,9 @@ exit:
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_bucket_str(struct ofputil_bucket *bucket, char *str_, uint8_t group_type,
-                  enum ofputil_protocol *usable_protocols)
+parse_bucket_str(struct ofputil_bucket *bucket, char *str_,
+                 const struct ofputil_port_map *port_map, uint8_t group_type,
+                 enum ofputil_protocol *usable_protocols)
 {
     char *pos, *key, *value;
     struct ofpbuf ofpacts;
@@ -1400,7 +1403,7 @@ parse_bucket_str(struct ofputil_bucket *bucket, char *str_, uint8_t group_type,
         if (!strcasecmp(key, "weight")) {
             error = str_to_u16(value, "weight", &bucket->weight);
         } else if (!strcasecmp(key, "watch_port")) {
-            if (!ofputil_port_from_string(value, &bucket->watch_port)
+            if (!ofputil_port_from_string(value, port_map, &bucket->watch_port)
                 || (ofp_to_u16(bucket->watch_port) >= ofp_to_u16(OFPP_MAX)
                     && bucket->watch_port != OFPP_ANY)) {
                 error = xasprintf("%s: invalid watch_port", value);
@@ -1436,7 +1439,7 @@ parse_bucket_str(struct ofputil_bucket *bucket, char *str_, uint8_t group_type,
     ds_chomp(&actions, ',');
 
     ofpbuf_init(&ofpacts, 0);
-    error = ofpacts_parse_actions(ds_cstr(&actions), &ofpacts,
+    error = ofpacts_parse_actions(ds_cstr(&actions), port_map, &ofpacts,
                                   usable_protocols);
     ds_destroy(&actions);
     if (error) {
@@ -1450,7 +1453,8 @@ parse_bucket_str(struct ofputil_bucket *bucket, char *str_, uint8_t group_type,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_select_group_field(char *s, struct field_array *fa,
+parse_select_group_field(char *s, const struct ofputil_port_map *port_map,
+                         struct field_array *fa,
                          enum ofputil_protocol *usable_protocols)
 {
     char *name, *value_str;
@@ -1467,7 +1471,7 @@ parse_select_group_field(char *s, struct field_array *fa,
             }
 
             if (*value_str) {
-                error = mf_parse_value(mf, value_str, &value);
+                error = mf_parse_value(mf, value_str, port_map, &value);
                 if (error) {
                     return error;
                 }
@@ -1510,6 +1514,7 @@ parse_select_group_field(char *s, struct field_array *fa,
 static char * OVS_WARN_UNUSED_RESULT
 parse_ofp_group_mod_str__(struct ofputil_group_mod *gm, int command,
                           char *string,
+                          const struct ofputil_port_map *port_map,
                           enum ofputil_protocol *usable_protocols)
 {
     enum {
@@ -1703,7 +1708,8 @@ parse_ofp_group_mod_str__(struct ofputil_group_mod *gm, int command,
                 error = xstrdup("fields are not needed");
                 goto out;
             }
-            error = parse_select_group_field(value, &gm->props.fields,
+            error = parse_select_group_field(value, port_map,
+                                             &gm->props.fields,
                                              usable_protocols);
             if (error) {
                 goto out;
@@ -1762,7 +1768,8 @@ parse_ofp_group_mod_str__(struct ofputil_group_mod *gm, int command,
         }
 
         bucket = xzalloc(sizeof(struct ofputil_bucket));
-        error = parse_bucket_str(bucket, bkt_str, gm->type, usable_protocols);
+        error = parse_bucket_str(bucket, bkt_str, port_map,
+                                 gm->type, usable_protocols);
         if (error) {
             free(bucket);
             goto out;
@@ -1794,11 +1801,12 @@ parse_ofp_group_mod_str__(struct ofputil_group_mod *gm, int command,
 char * OVS_WARN_UNUSED_RESULT
 parse_ofp_group_mod_str(struct ofputil_group_mod *gm, int command,
                         const char *str_,
+                        const struct ofputil_port_map *port_map,
                         enum ofputil_protocol *usable_protocols)
 {
     char *string = xstrdup(str_);
     char *error = parse_ofp_group_mod_str__(gm, command, string,
-                                            usable_protocols);
+                                            port_map, usable_protocols);
     free(string);
     return error;
 }
@@ -1808,7 +1816,9 @@ parse_ofp_group_mod_str(struct ofputil_group_mod *gm, int command,
  * missing command name is treated as "add".
  */
 char * OVS_WARN_UNUSED_RESULT
-parse_ofp_group_mod_file(const char *file_name, int command,
+parse_ofp_group_mod_file(const char *file_name,
+                         const struct ofputil_port_map *port_map,
+                         int command,
                          struct ofputil_group_mod **gms, size_t *n_gms,
                          enum ofputil_protocol *usable_protocols)
 {
@@ -1845,7 +1855,7 @@ parse_ofp_group_mod_file(const char *file_name, int command,
             *gms = new_gms;
         }
         error = parse_ofp_group_mod_str(&(*gms)[*n_gms], command, ds_cstr(&s),
-                                        &usable);
+                                        port_map, &usable);
         if (error) {
             size_t i;
 
@@ -1883,6 +1893,7 @@ parse_ofp_group_mod_file(const char *file_name, int command,
  * error.  The caller is responsible for freeing the returned string. */
 char * OVS_WARN_UNUSED_RESULT
 parse_ofp_bundle_file(const char *file_name,
+                      const struct ofputil_port_map *port_map,
                       struct ofputil_bundle_msg **bms, size_t *n_bms,
                       enum ofputil_protocol *usable_protocols)
 {
@@ -1929,7 +1940,8 @@ parse_ofp_bundle_file(const char *file_name,
 
         if (!strncmp(s, "flow", len)) {
             s += len;
-            error = parse_ofp_flow_mod_str(&(*bms)[*n_bms].fm, s, -2, &usable);
+            error = parse_ofp_flow_mod_str(&(*bms)[*n_bms].fm, s, port_map,
+                                           -2, &usable);
             if (error) {
                 break;
             }
@@ -1937,14 +1949,15 @@ parse_ofp_bundle_file(const char *file_name,
         } else if (!strncmp(s, "group", len)) {
             s += len;
             error = parse_ofp_group_mod_str(&(*bms)[*n_bms].gm, -2, s,
-                                            &usable);
+                                            port_map, &usable);
             if (error) {
                 break;
             }
             (*bms)[*n_bms].type = OFPTYPE_GROUP_MOD;
         } else if (!strncmp(s, "packet-out", len)) {
             s += len;
-            error = parse_ofp_packet_out_str(&(*bms)[*n_bms].po, s, &usable);
+            error = parse_ofp_packet_out_str(&(*bms)[*n_bms].po, s, port_map,
+                                             &usable);
             if (error) {
                 break;
             }
diff --git a/lib/ofp-print.c b/lib/ofp-print.c
index 7ca953100539..a8302fb045e0 100644
--- a/lib/ofp-print.c
+++ b/lib/ofp-print.c
@@ -68,7 +68,7 @@ ofp_packet_to_string(const void *data, size_t len, ovs_be32 packet_type)
     dp_packet_use_const(&buf, data, len);
     buf.packet_type = packet_type;
     flow_extract(&buf, &flow);
-    flow_format(&ds, &flow);
+    flow_format(&ds, &flow, NULL);
 
     l4_size = dp_packet_l4_size(&buf);
 
@@ -118,7 +118,7 @@ format_hex_arg(struct ds *s, const uint8_t *data, size_t len)
 
 static void
 ofp_print_packet_in(struct ds *string, const struct ofp_header *oh,
-                    int verbosity)
+                    const struct ofputil_port_map *port_map, int verbosity)
 {
     char reasonbuf[OFPUTIL_PACKET_IN_REASON_BUFSIZE];
     struct ofputil_packet_in_private pin;
@@ -144,7 +144,8 @@ ofp_print_packet_in(struct ds *string, const struct ofp_header *oh,
 
     ds_put_format(string, " total_len=%"PRIuSIZE" ", total_len);
 
-    match_format(&public->flow_metadata, string, OFP_DEFAULT_PRIORITY);
+    match_format(&public->flow_metadata, port_map,
+                 string, OFP_DEFAULT_PRIORITY);
 
     ds_put_format(string, " (via %s)",
                   ofputil_packet_in_reason_to_string(public->reason,
@@ -206,13 +207,13 @@ ofp_print_packet_in(struct ds *string, const struct ofp_header *oh,
 
     if (pin.actions_len) {
         ds_put_cstr(string, " continuation.actions=");
-        ofpacts_format(pin.actions, pin.actions_len, string);
+        ofpacts_format(pin.actions, pin.actions_len, port_map, string);
         ds_put_char(string, '\n');
     }
 
     if (pin.action_set_len) {
         ds_put_cstr(string, " continuation.action_set=");
-        ofpacts_format(pin.action_set, pin.action_set_len, string);
+        ofpacts_format(pin.action_set, pin.action_set_len, port_map, string);
         ds_put_char(string, '\n');
     }
 
@@ -233,7 +234,7 @@ ofp_print_packet_in(struct ds *string, const struct ofp_header *oh,
 
 static void
 ofp_print_packet_out(struct ds *string, const struct ofp_header *oh,
-                     int verbosity)
+                     const struct ofputil_port_map *port_map, int verbosity)
 {
     struct ofputil_packet_out po;
     struct ofpbuf ofpacts;
@@ -248,10 +249,10 @@ ofp_print_packet_out(struct ds *string, const struct ofp_header *oh,
     }
 
     ds_put_cstr(string, " in_port=");
-    ofputil_format_port(po.in_port, string);
+    ofputil_format_port(po.in_port, port_map, string);
 
     ds_put_cstr(string, " actions=");
-    ofpacts_format(po.ofpacts, po.ofpacts_len, string);
+    ofpacts_format(po.ofpacts, po.ofpacts_len, port_map, string);
 
     if (po.buffer_id == UINT32_MAX) {
         ds_put_format(string, " data_len=%"PRIuSIZE, po.packet_len);
@@ -445,7 +446,7 @@ ofp_print_phy_port(struct ds *string, const struct ofputil_phy_port *port)
     name[j] = '\0';
 
     ds_put_char(string, ' ');
-    ofputil_format_port(port->port_no, string);
+    ofputil_format_port(port->port_no, NULL, string);
     ds_put_format(string, "(%s): addr:"ETH_ADDR_FMT"\n",
                   name, ETH_ADDR_ARGS(port->hw_addr));
 
@@ -647,14 +648,15 @@ static void print_wild(struct ds *string, const char *leader, int is_wild,
 
 static void
 print_wild_port(struct ds *string, const char *leader, int is_wild,
-                int verbosity, ofp_port_t port)
+                int verbosity, ofp_port_t port,
+                const struct ofputil_port_map *port_map)
 {
     if (is_wild && verbosity < 2) {
         return;
     }
     ds_put_cstr(string, leader);
     if (!is_wild) {
-        ofputil_format_port(port, string);
+        ofputil_format_port(port, port_map, string);
     } else {
         ds_put_char(string, '*');
     }
@@ -681,15 +683,17 @@ print_ip_netmask(struct ds *string, const char *leader, ovs_be32 ip,
 }
 
 void
-ofp10_match_print(struct ds *f, const struct ofp10_match *om, int verbosity)
+ofp10_match_print(struct ds *f, const struct ofp10_match *om,
+                  const struct ofputil_port_map *port_map, int verbosity)
 {
-    char *s = ofp10_match_to_string(om, verbosity);
+    char *s = ofp10_match_to_string(om, port_map, verbosity);
     ds_put_cstr(f, s);
     free(s);
 }
 
 char *
-ofp10_match_to_string(const struct ofp10_match *om, int verbosity)
+ofp10_match_to_string(const struct ofp10_match *om,
+                      const struct ofputil_port_map *port_map, int verbosity)
 {
     struct ds f = DS_EMPTY_INITIALIZER;
     uint32_t w = ntohl(om->wildcards);
@@ -729,7 +733,7 @@ ofp10_match_to_string(const struct ofp10_match *om, int verbosity)
         }
     }
     print_wild_port(&f, "in_port=", w & OFPFW10_IN_PORT, verbosity,
-                    u16_to_ofp(ntohs(om->in_port)));
+                    u16_to_ofp(ntohs(om->in_port)), port_map);
     print_wild(&f, "dl_vlan=", w & OFPFW10_DL_VLAN, verbosity,
                "%d", ntohs(om->dl_vlan));
     print_wild(&f, "dl_vlan_pcp=", w & OFPFW10_DL_VLAN_PCP, verbosity,
@@ -802,7 +806,8 @@ ofp_print_flow_flags(struct ds *s, enum ofputil_flow_mod_flags flags)
 }
 
 static void
-ofp_print_flow_mod(struct ds *s, const struct ofp_header *oh, int verbosity)
+ofp_print_flow_mod(struct ds *s, const struct ofp_header *oh,
+                   const struct ofputil_port_map *port_map, int verbosity)
 {
     struct ofputil_flow_mod fm;
     struct ofpbuf ofpacts;
@@ -851,7 +856,7 @@ ofp_print_flow_mod(struct ds *s, const struct ofp_header *oh, int verbosity)
     ofpraw_decode(&raw, oh);
     if (verbosity >= 3 && raw == OFPRAW_OFPT10_FLOW_MOD) {
         const struct ofp10_flow_mod *ofm = ofpmsg_body(oh);
-        ofp10_match_print(s, &ofm->match, verbosity);
+        ofp10_match_print(s, &ofm->match, port_map, verbosity);
 
         /* ofp_print_match() doesn't print priority. */
         need_priority = true;
@@ -867,7 +872,7 @@ ofp_print_flow_mod(struct ds *s, const struct ofp_header *oh, int verbosity)
         /* nx_match_to_string() doesn't print priority. */
         need_priority = true;
     } else {
-        match_format(&fm.match, s, fm.priority);
+        match_format(&fm.match, port_map, s, fm.priority);
 
         /* match_format() does print priority. */
         need_priority = false;
@@ -900,7 +905,7 @@ ofp_print_flow_mod(struct ds *s, const struct ofp_header *oh, int verbosity)
     }
     if (fm.out_port != OFPP_ANY) {
         ds_put_format(s, "out_port:");
-        ofputil_format_port(fm.out_port, s);
+        ofputil_format_port(fm.out_port, port_map, s);
         ds_put_char(s, ' ');
     }
 
@@ -913,7 +918,7 @@ ofp_print_flow_mod(struct ds *s, const struct ofp_header *oh, int verbosity)
     ofp_print_flow_flags(s, fm.flags);
 
     ds_put_cstr(s, "actions=");
-    ofpacts_format(fm.ofpacts, fm.ofpacts_len, s);
+    ofpacts_format(fm.ofpacts, fm.ofpacts_len, port_map, s);
     ofpbuf_uninit(&ofpacts);
 }
 
@@ -975,7 +980,8 @@ ofp_flow_removed_reason_to_string(enum ofp_flow_removed_reason reason,
 }
 
 static void
-ofp_print_flow_removed(struct ds *string, const struct ofp_header *oh)
+ofp_print_flow_removed(struct ds *string, const struct ofp_header *oh,
+                       const struct ofputil_port_map *port_map)
 {
     char reasonbuf[OFP_FLOW_REMOVED_REASON_BUFSIZE];
     struct ofputil_flow_removed fr;
@@ -988,7 +994,7 @@ ofp_print_flow_removed(struct ds *string, const struct ofp_header *oh)
     }
 
     ds_put_char(string, ' ');
-    match_format(&fr.match, string, fr.priority);
+    match_format(&fr.match, port_map, string, fr.priority);
 
     ds_put_format(string, " reason=%s",
                   ofp_flow_removed_reason_to_string(fr.reason, reasonbuf,
@@ -1014,7 +1020,8 @@ ofp_print_flow_removed(struct ds *string, const struct ofp_header *oh)
 }
 
 static void
-ofp_print_port_mod(struct ds *string, const struct ofp_header *oh)
+ofp_print_port_mod(struct ds *string, const struct ofp_header *oh,
+                   const struct ofputil_port_map *port_map)
 {
     struct ofputil_port_mod pm;
     enum ofperr error;
@@ -1026,7 +1033,7 @@ ofp_print_port_mod(struct ds *string, const struct ofp_header *oh)
     }
 
     ds_put_cstr(string, " port: ");
-    ofputil_format_port(pm.port_no, string);
+    ofputil_format_port(pm.port_no, port_map, string);
     ds_put_format(string, ": addr:"ETH_ADDR_FMT"\n",
                   ETH_ADDR_ARGS(pm.hw_addr));
     if (!eth_addr64_is_zero(pm.hw_addr64)) {
@@ -1199,7 +1206,8 @@ ofp_print_table_status_message(struct ds *string, const struct ofp_header *oh)
 
 static void
 ofp_print_queue_get_config_request(struct ds *string,
-                                   const struct ofp_header *oh)
+                                   const struct ofp_header *oh,
+                                   const struct ofputil_port_map *port_map)
 {
     enum ofperr error;
     ofp_port_t port;
@@ -1212,7 +1220,7 @@ ofp_print_queue_get_config_request(struct ds *string,
     }
 
     ds_put_cstr(string, " port=");
-    ofputil_format_port(port, string);
+    ofputil_format_port(port, port_map, string);
 
     if (queue != OFPQ_ALL) {
         ds_put_cstr(string, " queue=");
@@ -1250,7 +1258,8 @@ compare_queues(const void *a_, const void *b_)
 
 static void
 ofp_print_queue_get_config_reply(struct ds *string,
-                                 const struct ofp_header *oh)
+                                 const struct ofp_header *oh,
+                                 const struct ofputil_port_map *port_map)
 {
     struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
 
@@ -1280,7 +1289,7 @@ ofp_print_queue_get_config_reply(struct ds *string,
             port = q->port;
 
             ds_put_cstr(string, "port=");
-            ofputil_format_port(port, string);
+            ofputil_format_port(port, port_map, string);
             ds_put_char(string, '\n');
         }
 
@@ -1579,7 +1588,8 @@ ofp_print_hello(struct ds *string, const struct ofp_header *oh)
 }
 
 static void
-ofp_print_error_msg(struct ds *string, const struct ofp_header *oh)
+ofp_print_error_msg(struct ds *string, const struct ofp_header *oh,
+                    const struct ofputil_port_map *port_map)
 {
     size_t len = ntohs(oh->length);
     struct ofpbuf payload;
@@ -1598,7 +1608,7 @@ ofp_print_error_msg(struct ds *string, const struct ofp_header *oh)
     if (error == OFPERR_OFPHFC_INCOMPATIBLE || error == OFPERR_OFPHFC_EPERM) {
         ds_put_printable(string, payload.data, payload.size);
     } else {
-        s = ofp_to_string(payload.data, payload.size, 1);
+        s = ofp_to_string(payload.data, payload.size, port_map, 1);
         ds_put_cstr(string, s);
         free(s);
     }
@@ -1647,7 +1657,8 @@ ofp_print_ofpst_desc_reply(struct ds *string, const struct ofp_header *oh)
 }
 
 static void
-ofp_print_flow_stats_request(struct ds *string, const struct ofp_header *oh)
+ofp_print_flow_stats_request(struct ds *string, const struct ofp_header *oh,
+                             const struct ofputil_port_map *port_map)
 {
     struct ofputil_flow_stats_request fsr;
     enum ofperr error;
@@ -1664,15 +1675,16 @@ ofp_print_flow_stats_request(struct ds *string, const struct ofp_header *oh)
 
     if (fsr.out_port != OFPP_ANY) {
         ds_put_cstr(string, " out_port=");
-        ofputil_format_port(fsr.out_port, string);
+        ofputil_format_port(fsr.out_port, port_map, string);
     }
 
     ds_put_char(string, ' ');
-    match_format(&fsr.match, string, OFP_DEFAULT_PRIORITY);
+    match_format(&fsr.match, port_map, string, OFP_DEFAULT_PRIORITY);
 }
 
 void
-ofp_print_flow_stats(struct ds *string, const struct ofputil_flow_stats *fs)
+ofp_print_flow_stats(struct ds *string, const struct ofputil_flow_stats *fs,
+                     const struct ofputil_port_map *port_map)
 {
     ds_put_format(string, " %scookie=%s0x%"PRIx64", %sduration=%s",
                   colors.param, colors.end, ntohll(fs->cookie),
@@ -1709,17 +1721,18 @@ ofp_print_flow_stats(struct ds *string, const struct ofputil_flow_stats *fs)
                       colors.param, colors.end, fs->hard_age);
     }
 
-    match_format(&fs->match, string, fs->priority);
+    match_format(&fs->match, port_map, string, fs->priority);
     if (string->string[string->length - 1] != ' ') {
         ds_put_char(string, ' ');
     }
 
     ds_put_format(string, "%sactions=%s", colors.actions, colors.end);
-    ofpacts_format(fs->ofpacts, fs->ofpacts_len, string);
+    ofpacts_format(fs->ofpacts, fs->ofpacts_len, port_map, string);
 }
 
 static void
-ofp_print_flow_stats_reply(struct ds *string, const struct ofp_header *oh)
+ofp_print_flow_stats_reply(struct ds *string, const struct ofp_header *oh,
+                           const struct ofputil_port_map *port_map)
 {
     struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
     struct ofpbuf ofpacts;
@@ -1737,7 +1750,7 @@ ofp_print_flow_stats_reply(struct ds *string, const struct ofp_header *oh)
             break;
         }
         ds_put_char(string, '\n');
-        ofp_print_flow_stats(string, &fs);
+        ofp_print_flow_stats(string, &fs, port_map);
      }
     ofpbuf_uninit(&ofpacts);
 }
@@ -1784,7 +1797,8 @@ print_port_stat_cond(struct ds *string, const char *leader, uint64_t stat)
 }
 
 static void
-ofp_print_ofpst_port_request(struct ds *string, const struct ofp_header *oh)
+ofp_print_ofpst_port_request(struct ds *string, const struct ofp_header *oh,
+                             const struct ofputil_port_map *port_map)
 {
     ofp_port_t ofp10_port;
     enum ofperr error;
@@ -1796,11 +1810,12 @@ ofp_print_ofpst_port_request(struct ds *string, const struct ofp_header *oh)
     }
 
     ds_put_cstr(string, " port_no=");
-    ofputil_format_port(ofp10_port, string);
+    ofputil_format_port(ofp10_port, port_map, string);
 }
 
 static void
 ofp_print_ofpst_port_reply(struct ds *string, const struct ofp_header *oh,
+                           const struct ofputil_port_map *port_map,
                            int verbosity)
 {
     ds_put_format(string, " %"PRIuSIZE" ports\n", ofputil_count_port_stats(oh));
@@ -1825,7 +1840,7 @@ ofp_print_ofpst_port_reply(struct ds *string, const struct ofp_header *oh,
         if (ofp_to_u16(ps.port_no) < 10) {
             ds_put_char(string, ' ');
         }
-        ofputil_format_port(ps.port_no, string);
+        ofputil_format_port(ps.port_no, port_map, string);
 
         ds_put_cstr(string, ": rx ");
         print_port_stat(string, "pkts=", ps.stats.rx_packets, 1);
@@ -1959,7 +1974,8 @@ ofp_print_queue_name(struct ds *string, uint32_t queue_id)
 }
 
 static void
-ofp_print_ofpst_queue_request(struct ds *string, const struct ofp_header *oh)
+ofp_print_ofpst_queue_request(struct ds *string, const struct ofp_header *oh,
+                              const struct ofputil_port_map *port_map)
 {
     struct ofputil_queue_stats_request oqsr;
     enum ofperr error;
@@ -1971,7 +1987,7 @@ ofp_print_ofpst_queue_request(struct ds *string, const struct ofp_header *oh)
     }
 
     ds_put_cstr(string, " port=");
-    ofputil_format_port(oqsr.port_no, string);
+    ofputil_format_port(oqsr.port_no, port_map, string);
 
     ds_put_cstr(string, " queue=");
     ofp_print_queue_name(string, oqsr.queue_id);
@@ -1979,6 +1995,7 @@ ofp_print_ofpst_queue_request(struct ds *string, const struct ofp_header *oh)
 
 static void
 ofp_print_ofpst_queue_reply(struct ds *string, const struct ofp_header *oh,
+                            const struct ofputil_port_map *port_map,
                             int verbosity)
 {
     ds_put_format(string, " %"PRIuSIZE" queues\n", ofputil_count_queue_stats(oh));
@@ -2000,7 +2017,7 @@ ofp_print_ofpst_queue_reply(struct ds *string, const struct ofp_header *oh,
         }
 
         ds_put_cstr(string, "  port ");
-        ofputil_format_port(qs.port_no, string);
+        ofputil_format_port(qs.port_no, port_map, string);
         ds_put_cstr(string, " queue ");
         ofp_print_queue_name(string, qs.queue_id);
         ds_put_cstr(string, ": ");
@@ -2021,7 +2038,8 @@ ofp_print_ofpst_queue_reply(struct ds *string, const struct ofp_header *oh,
 
 static void
 ofp_print_ofpst_port_desc_request(struct ds *string,
-                                  const struct ofp_header *oh)
+                                  const struct ofp_header *oh,
+                                  const struct ofputil_port_map *port_map)
 {
     enum ofperr error;
     ofp_port_t port;
@@ -2033,7 +2051,7 @@ ofp_print_ofpst_port_desc_request(struct ds *string,
     }
 
     ds_put_cstr(string, " port=");
-    ofputil_format_port(port, string);
+    ofputil_format_port(port, port_map, string);
 }
 
 static void
@@ -2384,7 +2402,8 @@ nx_flow_monitor_flags_to_name(uint32_t bit)
 
 static void
 ofp_print_nxst_flow_monitor_request(struct ds *string,
-                                    const struct ofp_header *oh)
+                                    const struct ofp_header *oh,
+                                    const struct ofputil_port_map *port_map)
 {
     struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
     for (;;) {
@@ -2405,7 +2424,7 @@ ofp_print_nxst_flow_monitor_request(struct ds *string,
 
         if (request.out_port != OFPP_NONE) {
             ds_put_cstr(string, " out_port=");
-            ofputil_format_port(request.out_port, string);
+            ofputil_format_port(request.out_port, port_map, string);
         }
 
         if (request.table_id != 0xff) {
@@ -2413,14 +2432,15 @@ ofp_print_nxst_flow_monitor_request(struct ds *string,
         }
 
         ds_put_char(string, ' ');
-        match_format(&request.match, string, OFP_DEFAULT_PRIORITY);
+        match_format(&request.match, port_map, string, OFP_DEFAULT_PRIORITY);
         ds_chomp(string, ' ');
     }
 }
 
 static void
 ofp_print_nxst_flow_monitor_reply(struct ds *string,
-                                  const struct ofp_header *oh)
+                                  const struct ofp_header *oh,
+                                  const struct ofputil_port_map *port_map)
 {
     uint64_t ofpacts_stub[1024 / 8];
     struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub);
@@ -2474,14 +2494,15 @@ ofp_print_nxst_flow_monitor_reply(struct ds *string,
         ds_put_format(string, " cookie=%#"PRIx64, ntohll(update.cookie));
 
         ds_put_char(string, ' ');
-        match_format(&update.match, string, OFP_DEFAULT_PRIORITY);
+        match_format(&update.match, port_map, string, OFP_DEFAULT_PRIORITY);
 
         if (update.ofpacts_len) {
             if (string->string[string->length - 1] != ' ') {
                 ds_put_char(string, ' ');
             }
             ds_put_cstr(string, "actions=");
-            ofpacts_format(update.ofpacts, update.ofpacts_len, string);
+            ofpacts_format(update.ofpacts, update.ofpacts_len, port_map,
+                           string);
         }
     }
 }
@@ -2558,7 +2579,8 @@ static void
 ofp_print_group(struct ds *s, uint32_t group_id, uint8_t type,
                 const struct ovs_list *p_buckets,
                 const struct ofputil_group_props *props,
-                enum ofp_version ofp_version, bool suppress_type)
+                enum ofp_version ofp_version, bool suppress_type,
+                const struct ofputil_port_map *port_map)
 {
     struct ofputil_bucket *bucket;
 
@@ -2602,14 +2624,16 @@ ofp_print_group(struct ds *s, uint32_t group_id, uint8_t type,
             ds_put_format(s, "weight:%"PRIu16",", bucket->weight);
         }
         if (bucket->watch_port != OFPP_NONE) {
-            ds_put_format(s, "watch_port:%"PRIu32",", bucket->watch_port);
+            ds_put_cstr(s, "watch_port:");
+            ofputil_format_port(bucket->watch_port, port_map, s);
+            ds_put_char(s, ',');
         }
         if (bucket->watch_group != OFPG_ANY) {
             ds_put_format(s, "watch_group:%"PRIu32",", bucket->watch_group);
         }
 
         ds_put_cstr(s, "actions=");
-        ofpacts_format(bucket->ofpacts, bucket->ofpacts_len, s);
+        ofpacts_format(bucket->ofpacts, bucket->ofpacts_len, port_map, s);
         ds_put_char(s, ',');
     }
 
@@ -2626,7 +2650,8 @@ ofp_print_ofpst_group_desc_request(struct ds *string,
 }
 
 static void
-ofp_print_group_desc(struct ds *s, const struct ofp_header *oh)
+ofp_print_group_desc(struct ds *s, const struct ofp_header *oh,
+                     const struct ofputil_port_map *port_map)
 {
     struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
     for (;;) {
@@ -2644,7 +2669,7 @@ ofp_print_group_desc(struct ds *s, const struct ofp_header *oh)
         ds_put_char(s, '\n');
         ds_put_char(s, ' ');
         ofp_print_group(s, gd.group_id, gd.type, &gd.buckets, &gd.props,
-                        oh->version, false);
+                        oh->version, false, port_map);
         ofputil_uninit_group_desc(&gd);
      }
 }
@@ -2746,7 +2771,8 @@ ofp_print_group_features(struct ds *string, const struct ofp_header *oh)
 
 static void
 ofp_print_group_mod__(struct ds *s, enum ofp_version ofp_version,
-                      const struct ofputil_group_mod *gm)
+                      const struct ofputil_group_mod *gm,
+                      const struct ofputil_port_map *port_map)
 {
     bool bucket_command = false;
 
@@ -2791,11 +2817,12 @@ ofp_print_group_mod__(struct ds *s, enum ofp_version ofp_version,
     }
 
     ofp_print_group(s, gm->group_id, gm->type, &gm->buckets, &gm->props,
-                    ofp_version, bucket_command);
+                    ofp_version, bucket_command, port_map);
 }
 
 static void
-ofp_print_group_mod(struct ds *s, const struct ofp_header *oh)
+ofp_print_group_mod(struct ds *s, const struct ofp_header *oh,
+                    const struct ofputil_port_map *port_map)
 {
     struct ofputil_group_mod gm;
     int error;
@@ -2805,7 +2832,7 @@ ofp_print_group_mod(struct ds *s, const struct ofp_header *oh)
         ofp_print_error(s, error);
         return;
     }
-    ofp_print_group_mod__(s, oh->version, &gm);
+    ofp_print_group_mod__(s, oh->version, &gm, port_map);
     ofputil_uninit_group_mod(&gm);
 }
 
@@ -3169,7 +3196,8 @@ ofp_print_bundle_ctrl(struct ds *s, const struct ofp_header *oh)
 }
 
 static void
-ofp_print_bundle_add(struct ds *s, const struct ofp_header *oh, int verbosity)
+ofp_print_bundle_add(struct ds *s, const struct ofp_header *oh,
+                     const struct ofputil_port_map *port_map, int verbosity)
 {
     int error;
     struct ofputil_bundle_add_msg badd;
@@ -3186,7 +3214,8 @@ ofp_print_bundle_add(struct ds *s, const struct ofp_header *oh, int verbosity)
     ofp_print_bit_names(s, badd.flags, bundle_flags_to_name, ' ');
 
     ds_put_char(s, '\n');
-    char *msg = ofp_to_string(badd.msg, ntohs(badd.msg->length), verbosity);
+    char *msg = ofp_to_string(badd.msg, ntohs(badd.msg->length), port_map,
+                              verbosity);
     ds_put_and_free_cstr(s, msg);
 }
 
@@ -3272,7 +3301,8 @@ ofp_print_tlv_table_reply(struct ds *s, const struct ofp_header *oh)
 /* This function will print the request forward message. The reason for
  * request forward is taken from rf.request.type */
 static void
-ofp_print_requestforward(struct ds *string, const struct ofp_header *oh)
+ofp_print_requestforward(struct ds *string, const struct ofp_header *oh,
+                         const struct ofputil_port_map *port_map)
 {
     struct ofputil_requestforward rf;
     enum ofperr error;
@@ -3288,7 +3318,7 @@ ofp_print_requestforward(struct ds *string, const struct ofp_header *oh)
     switch (rf.reason) {
     case OFPRFR_GROUP_MOD:
         ds_put_cstr(string, "group_mod");
-        ofp_print_group_mod__(string, oh->version, rf.group_mod);
+        ofp_print_group_mod__(string, oh->version, rf.group_mod, port_map);
         break;
 
     case OFPRFR_METER_MOD:
@@ -3391,7 +3421,8 @@ ofp_print_nxt_ct_flush_zone(struct ds *string, const struct nx_zone_id *nzi)
 
 
 static void
-ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
+ofp_to_string__(const struct ofp_header *oh,
+                const struct ofputil_port_map *port_map, enum ofpraw raw,
                 struct ds *string, int verbosity)
 {
     const void *msg = oh;
@@ -3415,7 +3446,7 @@ ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
         break;
 
     case OFPTYPE_GROUP_DESC_STATS_REPLY:
-        ofp_print_group_desc(string, oh);
+        ofp_print_group_desc(string, oh, port_map);
         break;
 
     case OFPTYPE_GROUP_FEATURES_STATS_REQUEST:
@@ -3427,7 +3458,7 @@ ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
         break;
 
     case OFPTYPE_GROUP_MOD:
-        ofp_print_group_mod(string, oh);
+        ofp_print_group_mod(string, oh, port_map);
         break;
 
     case OFPTYPE_TABLE_FEATURES_STATS_REQUEST:
@@ -3445,7 +3476,7 @@ ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
         break;
 
     case OFPTYPE_ERROR:
-        ofp_print_error_msg(string, oh);
+        ofp_print_error_msg(string, oh, port_map);
         break;
 
     case OFPTYPE_ECHO_REQUEST:
@@ -3472,11 +3503,11 @@ ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
         break;
 
     case OFPTYPE_PACKET_IN:
-        ofp_print_packet_in(string, oh, verbosity);
+        ofp_print_packet_in(string, oh, port_map, verbosity);
         break;
 
     case OFPTYPE_FLOW_REMOVED:
-        ofp_print_flow_removed(string, oh);
+        ofp_print_flow_removed(string, oh, port_map);
         break;
 
     case OFPTYPE_PORT_STATUS:
@@ -3484,15 +3515,15 @@ ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
         break;
 
     case OFPTYPE_PACKET_OUT:
-        ofp_print_packet_out(string, oh, verbosity);
+        ofp_print_packet_out(string, oh, port_map, verbosity);
         break;
 
     case OFPTYPE_FLOW_MOD:
-        ofp_print_flow_mod(string, oh, verbosity);
+        ofp_print_flow_mod(string, oh, port_map, verbosity);
         break;
 
     case OFPTYPE_PORT_MOD:
-        ofp_print_port_mod(string, oh);
+        ofp_print_port_mod(string, oh, port_map);
         break;
 
     case OFPTYPE_TABLE_MOD:
@@ -3508,11 +3539,11 @@ ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
         break;
 
     case OFPTYPE_QUEUE_GET_CONFIG_REQUEST:
-        ofp_print_queue_get_config_request(string, oh);
+        ofp_print_queue_get_config_request(string, oh, port_map);
         break;
 
     case OFPTYPE_QUEUE_GET_CONFIG_REPLY:
-        ofp_print_queue_get_config_reply(string, oh);
+        ofp_print_queue_get_config_reply(string, oh, port_map);
         break;
 
     case OFPTYPE_ROLE_REQUEST:
@@ -3524,7 +3555,7 @@ ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
         break;
 
     case OFPTYPE_REQUESTFORWARD:
-        ofp_print_requestforward(string, oh);
+        ofp_print_requestforward(string, oh, port_map);
         break;
 
     case OFPTYPE_TABLE_STATUS:
@@ -3560,7 +3591,7 @@ ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
     case OFPTYPE_FLOW_STATS_REQUEST:
     case OFPTYPE_AGGREGATE_STATS_REQUEST:
         ofp_print_stats(string, oh);
-        ofp_print_flow_stats_request(string, oh);
+        ofp_print_flow_stats_request(string, oh, port_map);
         break;
 
     case OFPTYPE_TABLE_STATS_REQUEST:
@@ -3569,12 +3600,12 @@ ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
 
     case OFPTYPE_PORT_STATS_REQUEST:
         ofp_print_stats(string, oh);
-        ofp_print_ofpst_port_request(string, oh);
+        ofp_print_ofpst_port_request(string, oh, port_map);
         break;
 
     case OFPTYPE_QUEUE_STATS_REQUEST:
         ofp_print_stats(string, oh);
-        ofp_print_ofpst_queue_request(string, oh);
+        ofp_print_ofpst_queue_request(string, oh, port_map);
         break;
 
     case OFPTYPE_DESC_STATS_REPLY:
@@ -3584,17 +3615,17 @@ ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
 
     case OFPTYPE_FLOW_STATS_REPLY:
         ofp_print_stats(string, oh);
-        ofp_print_flow_stats_reply(string, oh);
+        ofp_print_flow_stats_reply(string, oh, port_map);
         break;
 
     case OFPTYPE_QUEUE_STATS_REPLY:
         ofp_print_stats(string, oh);
-        ofp_print_ofpst_queue_reply(string, oh, verbosity);
+        ofp_print_ofpst_queue_reply(string, oh, port_map, verbosity);
         break;
 
     case OFPTYPE_PORT_STATS_REPLY:
         ofp_print_stats(string, oh);
-        ofp_print_ofpst_port_reply(string, oh, verbosity);
+        ofp_print_ofpst_port_reply(string, oh, port_map, verbosity);
         break;
 
     case OFPTYPE_TABLE_STATS_REPLY:
@@ -3609,7 +3640,7 @@ ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
 
     case OFPTYPE_PORT_DESC_STATS_REQUEST:
         ofp_print_stats(string, oh);
-        ofp_print_ofpst_port_desc_request(string, oh);
+        ofp_print_ofpst_port_desc_request(string, oh, port_map);
         break;
 
     case OFPTYPE_PORT_DESC_STATS_REPLY:
@@ -3651,11 +3682,11 @@ ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
         break;
 
     case OFPTYPE_FLOW_MONITOR_STATS_REQUEST:
-        ofp_print_nxst_flow_monitor_request(string, msg);
+        ofp_print_nxst_flow_monitor_request(string, msg, port_map);
         break;
 
     case OFPTYPE_FLOW_MONITOR_STATS_REPLY:
-        ofp_print_nxst_flow_monitor_reply(string, msg);
+        ofp_print_nxst_flow_monitor_reply(string, msg, port_map);
         break;
 
     case OFPTYPE_BUNDLE_CONTROL:
@@ -3663,7 +3694,7 @@ ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
         break;
 
     case OFPTYPE_BUNDLE_ADD_MESSAGE:
-        ofp_print_bundle_add(string, msg, verbosity);
+        ofp_print_bundle_add(string, msg, port_map, verbosity);
         break;
 
     case OFPTYPE_NXT_TLV_TABLE_MOD:
@@ -3678,7 +3709,7 @@ ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
         break;
 
     case OFPTYPE_NXT_RESUME:
-        ofp_print_packet_in(string, msg, verbosity);
+        ofp_print_packet_in(string, msg, port_map, verbosity);
         break;
     case OFPTYPE_IPFIX_BRIDGE_STATS_REQUEST:
         break;
@@ -3702,7 +3733,8 @@ ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw,
  * verbosity and higher numbers increase verbosity.  The caller is responsible
  * for freeing the string. */
 char *
-ofp_to_string(const void *oh_, size_t len, int verbosity)
+ofp_to_string(const void *oh_, size_t len,
+              const struct ofputil_port_map *port_map, int verbosity)
 {
     struct ds string = DS_EMPTY_INITIALIZER;
     const struct ofp_header *oh = oh_;
@@ -3735,7 +3767,7 @@ ofp_to_string(const void *oh_, size_t len, int verbosity)
 
         error = ofpraw_decode(&raw, oh);
         if (!error) {
-            ofp_to_string__(oh, raw, &string, verbosity);
+            ofp_to_string__(oh, port_map, raw, &string, verbosity);
             if (verbosity >= 5) {
                 if (ds_last(&string) != '\n') {
                     ds_put_char(&string, '\n');
@@ -3765,9 +3797,10 @@ print_and_free(FILE *stream, char *string)
  * given 'verbosity' level.  0 is a minimal amount of verbosity and higher
  * numbers increase verbosity. */
 void
-ofp_print(FILE *stream, const void *oh, size_t len, int verbosity)
+ofp_print(FILE *stream, const void *oh, size_t len,
+          const struct ofputil_port_map *port_map, int verbosity)
 {
-    print_and_free(stream, ofp_to_string(oh, len, verbosity));
+    print_and_free(stream, ofp_to_string(oh, len, port_map, verbosity));
 }
 
 /* Dumps the contents of the Ethernet frame in the 'len' bytes starting at
diff --git a/lib/ofp-util.c b/lib/ofp-util.c
index bdf89b6c3017..8ccdae127129 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -33,6 +33,7 @@
 #include "id-pool.h"
 #include "openflow/netronome-ext.h"
 #include "openvswitch/dynamic-string.h"
+#include "openvswitch/json.h"
 #include "openvswitch/meta-flow.h"
 #include "openvswitch/ofp-actions.h"
 #include "openvswitch/ofp-errors.h"
@@ -7225,7 +7226,8 @@ ofputil_port_to_ofp11(ofp_port_t ofp10_port)
 
 /* Stores the port number represented by 's' into '*portp'.  's' may be an
  * integer or, for reserved ports, the standard OpenFlow name for the port
- * (e.g. "LOCAL").
+ * (e.g. "LOCAL").  If 'port_map' is nonnull, also accepts names in it (quoted
+ * or unquoted).
  *
  * Returns true if successful, false if 's' is not a valid OpenFlow port number
  * or name.  The caller should issue an error message in this case, because
@@ -7237,7 +7239,9 @@ ofputil_port_to_ofp11(ofp_port_t ofp10_port)
  * of OpenFlow 1.1+ port numbers, mapping those port numbers into the 16-bit
  * range as described in include/openflow/openflow-1.1.h. */
 bool
-ofputil_port_from_string(const char *s, ofp_port_t *portp)
+ofputil_port_from_string(const char *s,
+                         const struct ofputil_port_map *port_map,
+                         ofp_port_t *portp)
 {
     unsigned int port32; /* int is at least 32 bits wide. */
 
@@ -7256,7 +7260,8 @@ ofputil_port_from_string(const char *s, ofp_port_t *portp)
         } else if (port32 <= ofp_to_u16(OFPP_LAST_RESV)) {
             char name[OFP10_MAX_PORT_NAME_LEN];
 
-            ofputil_port_to_string(u16_to_ofp(port32), name, sizeof name);
+            ofputil_port_to_string(u16_to_ofp(port32), NULL,
+                                   name, sizeof name);
             VLOG_WARN_ONCE("referring to port %s as %"PRIu32" is deprecated "
                            "for compatibility with OpenFlow 1.1 and later",
                            name, port32);
@@ -7289,20 +7294,89 @@ ofputil_port_from_string(const char *s, ofp_port_t *portp)
                 return true;
             }
         }
+
+        ofp_port_t ofp_port = OFPP_NONE;
+        if (s[0] != '"') {
+            ofp_port = ofputil_port_map_get_number(port_map, s);
+        } else {
+            size_t length = strlen(s);
+            char *name = NULL;
+            if (length > 1
+                && s[length - 1] == '"'
+                && json_string_unescape(s + 1, length - 2, &name)) {
+                ofp_port = ofputil_port_map_get_number(port_map, name);
+            }
+            free(name);
+        }
+        if (ofp_port != OFPP_NONE) {
+            *portp = ofp_port;
+            return true;
+        }
+
         return false;
     }
 }
 
+const char *
+ofputil_port_get_reserved_name(ofp_port_t port)
+{
+    switch (port) {
+#define OFPUTIL_NAMED_PORT(NAME) case OFPP_##NAME: return #NAME;
+        OFPUTIL_NAMED_PORTS
+#undef OFPUTIL_NAMED_PORT
+
+    default:
+        return NULL;
+    }
+}
+
+/* A port name doesn't need to be quoted if it is alphanumeric and starts with
+ * a letter. */
+static bool
+port_name_needs_quotes(const char *port_name)
+{
+    if (!isalpha((unsigned char) port_name[0])) {
+        return true;
+    }
+
+    for (const char *p = port_name + 1; *p; p++) {
+        if (!isalnum((unsigned char) *p)) {
+            return true;
+        }
+    }
+    return false;
+}
+
+static void
+put_port_name(const char *port_name, struct ds *s)
+{
+    if (port_name_needs_quotes(port_name)) {
+        json_string_escape(port_name, s);
+    } else {
+        ds_put_cstr(s, port_name);
+    }
+}
+
 /* Appends to 's' a string representation of the OpenFlow port number 'port'.
  * Most ports' string representation is just the port number, but for special
  * ports, e.g. OFPP_LOCAL, it is the name, e.g. "LOCAL". */
 void
-ofputil_format_port(ofp_port_t port, struct ds *s)
+ofputil_format_port(ofp_port_t port, const struct ofputil_port_map *port_map,
+                    struct ds *s)
 {
-    char name[OFP10_MAX_PORT_NAME_LEN];
+    const char *reserved_name = ofputil_port_get_reserved_name(port);
+    if (reserved_name) {
+        ds_put_cstr(s, reserved_name);
+        return;
+    }
 
-    ofputil_port_to_string(port, name, sizeof name);
-    ds_put_cstr(s, name);
+    const char *port_name = ofputil_port_map_get_name(port_map, port);
+    if (port_name) {
+        put_port_name(port_name, s);
+        return;
+    }
+
+    ds_put_format(s, "%"PRIu32, port);
 }
 
 /* Puts in the 'bufsize' byte in 'namebuf' a null-terminated string
@@ -7311,22 +7385,149 @@ 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[OFP10_MAX_PORT_NAME_LEN], size_t bufsize)
+                       const struct ofputil_port_map *port_map,
+                       char *namebuf, size_t bufsize)
 {
-    switch (port) {
-#define OFPUTIL_NAMED_PORT(NAME)                        \
-        case OFPP_##NAME:                               \
-            ovs_strlcpy(namebuf, #NAME, bufsize);       \
-            break;
-        OFPUTIL_NAMED_PORTS
-#undef OFPUTIL_NAMED_PORT
+    const char *reserved_name = ofputil_port_get_reserved_name(port);
+    if (reserved_name) {
+        ovs_strlcpy(namebuf, reserved_name, bufsize);
+        return;
+    }
 
-    default:
-        snprintf(namebuf, bufsize, "%"PRIu32, port);
-        break;
+    const char *port_name = ofputil_port_map_get_name(port_map, port);
+    if (port_name) {
+        struct ds s = DS_EMPTY_INITIALIZER;
+        put_port_name(port_name, &s);
+        ovs_strlcpy(namebuf, ds_cstr(&s), bufsize);
+        ds_destroy(&s);
+        return;
     }
+
+    snprintf(namebuf, bufsize, "%"PRIu32, port);
 }
+
+/* ofputil_port_map.  */
+struct ofputil_port_map_node {
+    struct hmap_node name_node;
+    struct hmap_node number_node;
+    ofp_port_t ofp_port;        /* Port number. */
+    char *name;                 /* Port name. */
+
+    /* OpenFlow doesn't require port names to be unique, although that's the
+     * only sensible way.  However, even in Open vSwitch it's possible for two
+     * ports to appear to have the same name if their names are longer than the
+     * maximum length supported by a given version of OpenFlow.  So, we guard
+     * against duplicate names to avoid giving unexpected results in this
+     * corner case.
+     *
+     * OpenFlow does require port numbers to be unique.  We check for duplicate
+     * ports numbers just in case a switch has a bug. */
+    bool duplicate;
+};
 
+void
+ofputil_port_map_init(struct ofputil_port_map *map)
+{
+    hmap_init(&map->by_name);
+    hmap_init(&map->by_number);
+}
+
+static struct ofputil_port_map_node *
+ofputil_port_map_find_by_name(const struct ofputil_port_map *map,
+                              const char *name)
+{
+    struct ofputil_port_map_node *node;
+
+    HMAP_FOR_EACH_WITH_HASH (node, name_node, hash_string(name, 0),
+                             &map->by_name) {
+        if (!strcmp(name, node->name)) {
+            return node;
+        }
+    }
+    return NULL;
+}
+
+static struct ofputil_port_map_node *
+ofputil_port_map_find_by_number(const struct ofputil_port_map *map,
+                                ofp_port_t ofp_port)
+{
+    struct ofputil_port_map_node *node;
+
+    HMAP_FOR_EACH_IN_BUCKET (node, number_node, hash_ofp_port(ofp_port),
+                             &map->by_number) {
+        if (node->ofp_port == ofp_port) {
+            return node;
+        }
+    }
+    return NULL;
+}
+
+void
+ofputil_port_map_put(struct ofputil_port_map *map,
+                     ofp_port_t ofp_port, const char *name)
+{
+    struct ofputil_port_map_node *node;
+
+    /* Look for duplicate name. */
+    node = ofputil_port_map_find_by_name(map, name);
+    if (node) {
+        if (node->ofp_port != ofp_port) {
+            node->duplicate = true;
+        }
+        return;
+    }
+
+    /* Look for duplicate number. */
+    node = ofputil_port_map_find_by_number(map, ofp_port);
+    if (node) {
+        node->duplicate = true;
+        return;
+    }
+
+    /* Add new node. */
+    node = xmalloc(sizeof *node);
+    hmap_insert(&map->by_number, &node->number_node, hash_ofp_port(ofp_port));
+    hmap_insert(&map->by_name, &node->name_node, hash_string(name, 0));
+    node->ofp_port = ofp_port;
+    node->name = xstrdup(name);
+    node->duplicate = false;
+}
+
+const char *
+ofputil_port_map_get_name(const struct ofputil_port_map *map,
+                          ofp_port_t ofp_port)
+{
+    struct ofputil_port_map_node *node
+        = map ? ofputil_port_map_find_by_number(map, ofp_port) : NULL;
+    return node && !node->duplicate ? node->name : NULL;
+}
+
+ofp_port_t
+ofputil_port_map_get_number(const struct ofputil_port_map *map,
+                            const char *name)
+{
+    struct ofputil_port_map_node *node
+        = map ? ofputil_port_map_find_by_name(map, name) : NULL;
+    return node && !node->duplicate ? node->ofp_port : OFPP_NONE;
+}
+
+void
+ofputil_port_map_destroy(struct ofputil_port_map *map)
+{
+    if (map) {
+        struct ofputil_port_map_node *node, *next;
+
+        HMAP_FOR_EACH_SAFE (node, next, name_node, &map->by_name) {
+            hmap_remove(&map->by_name, &node->name_node);
+            hmap_remove(&map->by_number, &node->number_node);
+            free(node->name);
+            free(node);
+        }
+        hmap_destroy(&map->by_name);
+        hmap_destroy(&map->by_number);
+    }
+}
+
 /* Stores the group id represented by 's' into '*group_idp'.  's' may be an
  * integer or, for reserved group IDs, the standard OpenFlow name for the group
  * (either "ANY" or "ALL").
@@ -7502,13 +7703,15 @@ ofputil_normalize_match__(struct match *match, bool may_log)
     /* Log any changes. */
     if (!flow_wildcards_equal(&wc, &match->wc)) {
         bool log = may_log && !VLOG_DROP_INFO(&bad_ofmsg_rl);
-        char *pre = log ? match_to_string(match, OFP_DEFAULT_PRIORITY) : NULL;
+        char *pre = (log
+                     ? match_to_string(match, NULL, OFP_DEFAULT_PRIORITY)
+                     : NULL);
 
         match->wc = wc;
         match_zero_wildcarded_fields(match);
 
         if (log) {
-            char *post = match_to_string(match, OFP_DEFAULT_PRIORITY);
+            char *post = match_to_string(match, NULL, OFP_DEFAULT_PRIORITY);
             VLOG_INFO("normalization changed ofp_match, details:");
             VLOG_INFO(" pre: %s", pre);
             VLOG_INFO("post: %s", post);
diff --git a/lib/vconn.c b/lib/vconn.c
index 57e623a920cc..6997eaa96e2c 100644
--- a/lib/vconn.c
+++ b/lib/vconn.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008-2016 Nicira, Inc.
+ * Copyright (c) 2008-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.
@@ -495,7 +495,7 @@ vcs_recv_hello(struct vconn *vconn)
             ofpbuf_delete(b);
             return;
         } else {
-            char *s = ofp_to_string(b->data, b->size, 1);
+            char *s = ofp_to_string(b->data, b->size, NULL, 1);
             VLOG_WARN_RL(&bad_ofmsg_rl,
                          "%s: received message while expecting hello: %s",
                          vconn->name, s);
@@ -641,7 +641,7 @@ do_recv(struct vconn *vconn, struct ofpbuf **msgp)
     if (!retval) {
         COVERAGE_INC(vconn_received);
         if (VLOG_IS_DBG_ENABLED()) {
-            char *s = ofp_to_string((*msgp)->data, (*msgp)->size, 1);
+            char *s = ofp_to_string((*msgp)->data, (*msgp)->size, NULL, 1);
             VLOG_DBG_RL(&ofmsg_rl, "%s: received: %s", vconn->name, s);
             free(s);
         }
@@ -681,7 +681,7 @@ do_send(struct vconn *vconn, struct ofpbuf *msg)
         COVERAGE_INC(vconn_sent);
         retval = (vconn->vclass->send)(vconn, msg);
     } else {
-        char *s = ofp_to_string(msg->data, msg->size, 1);
+        char *s = ofp_to_string(msg->data, msg->size, NULL, 1);
         retval = (vconn->vclass->send)(vconn, msg);
         if (retval != EAGAIN) {
             VLOG_DBG_RL(&ofmsg_rl, "%s: sent (%s): %s",
@@ -970,7 +970,7 @@ recv_flow_stats_reply(struct vconn *vconn, ovs_be32 send_xid,
             error = ofptype_decode(&type, reply->data);
             if (error || type != OFPTYPE_FLOW_STATS_REPLY) {
                 VLOG_WARN_RL(&rl, "received bad reply: %s",
-                             ofp_to_string(reply->data, reply->size, 1));
+                             ofp_to_string(reply->data, reply->size, NULL, 1));
                 return EPROTO;
             }
         }
diff --git a/ofproto/bond.c b/ofproto/bond.c
index 441ca44faf3f..21370b5f9a47 100644
--- a/ofproto/bond.c
+++ b/ofproto/bond.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Nicira, Inc.
+ * Copyright (c) 2008-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.
@@ -372,7 +372,7 @@ update_recirc_rules__(struct bond *bond)
                                                    RECIRC_RULE_PRIORITY, 0,
                                                    &ofpacts, pr_op->pr_rule);
             if (error) {
-                char *err_s = match_to_string(&pr_op->match,
+                char *err_s = match_to_string(&pr_op->match, NULL,
                                               RECIRC_RULE_PRIORITY);
 
                 VLOG_ERR("failed to add post recirculation flow %s", err_s);
@@ -385,7 +385,7 @@ update_recirc_rules__(struct bond *bond)
                                                       &pr_op->match,
                                                       RECIRC_RULE_PRIORITY);
             if (error) {
-                char *err_s = match_to_string(&pr_op->match,
+                char *err_s = match_to_string(&pr_op->match, NULL,
                                               RECIRC_RULE_PRIORITY);
 
                 VLOG_ERR("failed to remove post recirculation flow %s", err_s);
diff --git a/ofproto/ofproto-dpif-trace.c b/ofproto/ofproto-dpif-trace.c
index d1cb1ddbba5b..c7f0e48caa16 100644
--- a/ofproto/ofproto-dpif-trace.c
+++ b/ofproto/ofproto-dpif-trace.c
@@ -253,9 +253,16 @@ parse_flow_and_packet(int argc, const char *argv[],
             goto exit;
         }
 
+        struct ofputil_port_map map = OFPUTIL_PORT_MAP_INITIALIZER(&map);
+        const struct ofport *ofport;
+        HMAP_FOR_EACH (ofport, hmap_node, &(*ofprotop)->up.ports) {
+            ofputil_port_map_put(&map, ofport->ofp_port,
+                                 netdev_get_name(ofport->netdev));
+        }
         err = parse_ofp_exact_flow(flow, NULL,
                                    ofproto_get_tun_tab(&(*ofprotop)->up),
-                                   argv[argc - 1], NULL);
+                                   argv[argc - 1], &map);
+        ofputil_port_map_destroy(&map);
         if (err) {
             m_err = xasprintf("Bad openflow flow syntax: %s", err);
             free(err);
@@ -336,7 +343,8 @@ ofproto_unixctl_trace_actions(struct unixctl_conn *conn, int argc,
     ofpbuf_init(&ofpacts, 0);
 
     /* Parse actions. */
-    error = ofpacts_parse_actions(argv[--argc], &ofpacts, &usable_protocols);
+    error = ofpacts_parse_actions(argv[--argc], NULL,
+                                  &ofpacts, &usable_protocols);
     if (error) {
         unixctl_command_reply_error(conn, error);
         free(error);
@@ -444,7 +452,7 @@ ofproto_trace(struct ofproto_dpif *ofproto, struct flow *flow,
      * xlate_in_init() initializes actset_output to OFPP_UNSET. */
     struct flow initial_flow = xin.flow;
     ds_put_cstr(output, "Flow: ");
-    flow_format(output, &initial_flow);
+    flow_format(output, &initial_flow, NULL);
     ds_put_char(output, '\n');
 
     struct xlate_out xout;
@@ -456,14 +464,14 @@ ofproto_trace(struct ofproto_dpif *ofproto, struct flow *flow,
     if (flow_equal(&initial_flow, &xin.flow)) {
         ds_put_cstr(output, "unchanged");
     } else {
-        flow_format(output, &xin.flow);
+        flow_format(output, &xin.flow, NULL);
     }
     ds_put_char(output, '\n');
 
     ds_put_cstr(output, "Megaflow: ");
     struct match match;
     match_init(&match, flow, &wc);
-    match_format(&match, output, OFP_DEFAULT_PRIORITY);
+    match_format(&match, NULL, output, OFP_DEFAULT_PRIORITY);
     ds_put_char(output, '\n');
 
     ds_put_cstr(output, "Datapath actions: ");
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index 39fed7e0b303..aa8ae1334e8f 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -629,7 +629,7 @@ xlate_report_error(const struct xlate_ctx *ctx, const char *format, ...)
         oftrace_report(ctx->xin->trace, OFT_ERROR, ds_cstr(&s));
     } else {
         ds_put_cstr(&s, " while processing ");
-        flow_format(&s, &ctx->base_flow);
+        flow_format(&s, &ctx->base_flow, NULL);
         ds_put_format(&s, " on bridge %s", ctx->xbridge->name);
         VLOG_WARN("%s", ds_cstr(&s));
     }
@@ -676,7 +676,7 @@ xlate_report_actions(const struct xlate_ctx *ctx, enum oftrace_node_type type,
     if (OVS_UNLIKELY(ctx->xin->trace)) {
         struct ds s = DS_EMPTY_INITIALIZER;
         ds_put_format(&s, "%s: ", title);
-        ofpacts_format(ofpacts, ofpacts_len, &s);
+        ofpacts_format(ofpacts, ofpacts_len, NULL, &s);
         oftrace_report(ctx->xin->trace, type, ds_cstr(&s));
         ds_destroy(&s);
     }
@@ -697,7 +697,7 @@ xlate_report_action_set(const struct xlate_ctx *ctx, const char *verb)
         ofpacts_execute_action_set(&action_list, &ctx->action_set);
         if (action_list.size) {
             struct ds s = DS_EMPTY_INITIALIZER;
-            ofpacts_format(action_list.data, action_list.size, &s);
+            ofpacts_format(action_list.data, action_list.size, NULL, &s);
             xlate_report(ctx, OFT_DETAIL, "action set %s: %s",
                          verb, ds_cstr(&s));
             ds_destroy(&s);
@@ -736,7 +736,7 @@ xlate_report_table(const struct xlate_ctx *ctx, struct rule_dpif *rule,
     } else {
         minimatch_format(&rule->up.cr.match,
                          ofproto_get_tun_tab(&ctx->xin->ofproto->up),
-                         &s, OFP_DEFAULT_PRIORITY);
+                         NULL, &s, OFP_DEFAULT_PRIORITY);
         if (ds_last(&s) != ' ') {
             ds_put_cstr(&s, ", ");
         }
@@ -767,7 +767,7 @@ xlate_report_subfield(const struct xlate_ctx *ctx,
         if (sf->ofs == 0 && sf->n_bits >= sf->field->n_bits) {
             union mf_value value;
             mf_get_value(sf->field, &ctx->xin->flow, &value);
-            mf_format(sf->field, &value, NULL, &s);
+            mf_format(sf->field, &value, NULL, NULL, &s);
         } else {
             union mf_subvalue cst;
             mf_read_subfield(sf, &ctx->xin->flow, &cst);
@@ -4610,7 +4610,7 @@ xlate_output_trunc_action(struct xlate_ctx *ctx,
     case OFPP_ALL:
     case OFPP_CONTROLLER:
     case OFPP_NONE:
-        ofputil_port_to_string(port, name, sizeof name);
+        ofputil_port_to_string(port, NULL, name, sizeof name);
         xlate_report(ctx, OFT_WARN,
                      "output_trunc does not support port: %s", name);
         break;
@@ -4624,7 +4624,7 @@ xlate_output_trunc_action(struct xlate_ctx *ctx,
                 /* Since truncate happens at its following output action, if
                  * the output port is a patch port, the behavior is somehow
                  * unpredictable.  For simplicity, disallow this case. */
-                ofputil_port_to_string(port, name, sizeof name);
+                ofputil_port_to_string(port, NULL, name, sizeof name);
                 xlate_report_error(ctx, "output_trunc does not support "
                                    "patch port %s", name);
                 break;
@@ -4757,7 +4757,7 @@ xlate_learn_action(struct xlate_ctx *ctx, const struct ofpact_learn *learn)
         if (OVS_UNLIKELY(ctx->xin->trace)) {
             struct ds s = DS_EMPTY_INITIALIZER;
             ds_put_format(&s, "table=%"PRIu8" ", fm.table_id);
-            match_format(&fm.match, &s, OFP_DEFAULT_PRIORITY);
+            match_format(&fm.match, NULL, &s, OFP_DEFAULT_PRIORITY);
             ds_chomp(&s, ' ');
             ds_put_format(&s, " priority=%d", fm.priority);
             if (fm.new_cookie) {
@@ -4773,7 +4773,7 @@ xlate_learn_action(struct xlate_ctx *ctx, const struct ofpact_learn *learn)
                 ds_put_cstr(&s, " send_flow_rem");
             }
             ds_put_cstr(&s, " actions=");
-            ofpacts_format(fm.ofpacts, fm.ofpacts_len, &s);
+            ofpacts_format(fm.ofpacts, fm.ofpacts_len, NULL, &s);
             xlate_report(ctx, OFT_DETAIL, "%s", ds_cstr(&s));
             ds_destroy(&s);
         }
@@ -5554,7 +5554,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
 
         if (OVS_UNLIKELY(ctx->xin->trace)) {
             struct ds s = DS_EMPTY_INITIALIZER;
-            ofpacts_format(a, OFPACT_ALIGN(a->len), &s);
+            ofpacts_format(a, OFPACT_ALIGN(a->len), NULL, &s);
             xlate_report(ctx, OFT_ACTION, "%s", ds_cstr(&s));
             ds_destroy(&s);
         }
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index dc5f004cd92d..369e5f4aaa2c 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -4977,7 +4977,7 @@ ofproto_unixctl_fdb_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
         char name[OFP10_MAX_PORT_NAME_LEN];
 
         ofputil_port_to_string(ofbundle_get_a_port(bundle)->up.ofp_port,
-                               name, sizeof name);
+                               NULL, name, sizeof name);
         ds_put_format(&ds, "%5s  %4d  "ETH_ADDR_FMT"  %3d\n",
                       name, e->vlan, ETH_ADDR_ARGS(e->mac),
                       mac_entry_age(ofproto->ml, e));
@@ -5019,7 +5019,7 @@ ofproto_unixctl_mcast_snooping_show(struct unixctl_conn *conn,
 
             bundle = b->port;
             ofputil_port_to_string(ofbundle_get_a_port(bundle)->up.ofp_port,
-                                   name, sizeof name);
+                                   NULL, name, sizeof name);
             ds_put_format(&ds, "%5s  %4d  ", name, grp->vlan);
             ipv6_format_mapped(&grp->addr, &ds);
             ds_put_format(&ds, "         %3d\n",
@@ -5033,7 +5033,7 @@ ofproto_unixctl_mcast_snooping_show(struct unixctl_conn *conn,
 
         bundle = mrouter->port;
         ofputil_port_to_string(ofbundle_get_a_port(bundle)->up.ofp_port,
-                               name, sizeof name);
+                               NULL, name, sizeof name);
         ds_put_format(&ds, "%5s  %4d  querier             %3d\n",
                       name, mrouter->vlan,
                       mcast_mrouter_age(ofproto->ms, mrouter));
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index d5410fd1b20f..45666d2ac1ed 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -4379,11 +4379,11 @@ flow_stats_ds(struct ofproto *ofproto, struct rule *rule, struct ds *results)
     ds_put_format(results, "duration=%llds, ", (time_msec() - created) / 1000);
     ds_put_format(results, "n_packets=%"PRIu64", ", packet_count);
     ds_put_format(results, "n_bytes=%"PRIu64", ", byte_count);
-    cls_rule_format(&rule->cr, ofproto_get_tun_tab(ofproto), results);
+    cls_rule_format(&rule->cr, ofproto_get_tun_tab(ofproto), NULL, results);
     ds_put_char(results, ',');
 
     ds_put_cstr(results, "actions=");
-    ofpacts_format(actions->ofpacts, actions->ofpacts_len, results);
+    ofpacts_format(actions->ofpacts, actions->ofpacts_len, NULL, results);
 
     ds_put_cstr(results, "\n");
 }
diff --git a/ofproto/tunnel.c b/ofproto/tunnel.c
index e285d5464b84..1129547d7e94 100644
--- a/ofproto/tunnel.c
+++ b/ofproto/tunnel.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013, 2014, 2015 Nicira, Inc.
+/* Copyright (c) 2013, 2014, 2015, 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.
@@ -301,7 +301,7 @@ tnl_port_receive(const struct flow *flow) OVS_EXCLUDED(rwlock)
     tnl_port = tnl_find(flow);
     ofport = tnl_port ? tnl_port->ofport : NULL;
     if (!tnl_port) {
-        char *flow_str = flow_to_string(flow);
+        char *flow_str = flow_to_string(flow, NULL);
 
         VLOG_WARN_RL(&rl, "receive tunnel port not found (%s)", flow_str);
         free(flow_str);
@@ -309,11 +309,11 @@ tnl_port_receive(const struct flow *flow) OVS_EXCLUDED(rwlock)
     }
 
     if (!VLOG_DROP_DBG(&dbg_rl)) {
-        pre_flow_str = flow_to_string(flow);
+        pre_flow_str = flow_to_string(flow, NULL);
     }
 
     if (pre_flow_str) {
-        char *post_flow_str = flow_to_string(flow);
+        char *post_flow_str = flow_to_string(flow, NULL);
         char *tnl_str = tnl_port_fmt(tnl_port);
         VLOG_DBG("flow received\n"
                  "%s"
@@ -408,7 +408,7 @@ tnl_port_send(const struct ofport_dpif *ofport, struct flow *flow,
     ovs_assert(cfg);
 
     if (!VLOG_DROP_DBG(&dbg_rl)) {
-        pre_flow_str = flow_to_string(flow);
+        pre_flow_str = flow_to_string(flow, NULL);
     }
 
     if (!cfg->ip_src_flow) {
@@ -467,7 +467,7 @@ tnl_port_send(const struct ofport_dpif *ofport, struct flow *flow,
     }
 
     if (pre_flow_str) {
-        char *post_flow_str = flow_to_string(flow);
+        char *post_flow_str = flow_to_string(flow, NULL);
         char *tnl_str = tnl_port_fmt(tnl_port);
         VLOG_DBG("flow sent\n"
                  "%s"
diff --git a/ovn/controller/ofctrl.c b/ovn/controller/ofctrl.c
index 417fdc9f52b2..32631bcc82b8 100644
--- a/ovn/controller/ofctrl.c
+++ b/ovn/controller/ofctrl.c
@@ -286,7 +286,7 @@ recv_S_TLV_TABLE_REQUESTED(const struct ofp_header *oh, enum ofptype type,
         VLOG_ERR("switch refused to allocate Geneve option (%s)",
                  ofperr_to_string(ofperr_decode_msg(oh, NULL)));
     } else {
-        char *s = ofp_to_string(oh, ntohs(oh->length), 1);
+        char *s = ofp_to_string(oh, ntohs(oh->length), NULL, 1);
         VLOG_ERR("unexpected reply to TLV table request (%s)", s);
         free(s);
     }
@@ -340,7 +340,7 @@ recv_S_TLV_TABLE_MOD_SENT(const struct ofp_header *oh, enum ofptype type,
             goto error;
         }
     } else {
-        char *s = ofp_to_string(oh, ntohs(oh->length), 1);
+        char *s = ofp_to_string(oh, ntohs(oh->length), NULL, 1);
         VLOG_ERR("unexpected reply to Geneve option allocation request (%s)",
                  s);
         free(s);
@@ -513,7 +513,7 @@ ofctrl_run(const struct ovsrec_bridge *br_int, struct shash *pending_ct_zones)
                     OVS_NOT_REACHED();
                 }
             } else {
-                char *s = ofp_to_string(oh, ntohs(oh->length), 1);
+                char *s = ofp_to_string(oh, ntohs(oh->length), NULL, 1);
                 VLOG_WARN("could not decode OpenFlow message (%s): %s",
                           ofperr_to_string(error), s);
                 free(s);
@@ -573,7 +573,7 @@ log_openflow_rl(struct vlog_rate_limit *rl, enum vlog_level level,
                 const struct ofp_header *oh, const char *title)
 {
     if (!vlog_should_drop(&this_module, level, rl)) {
-        char *s = ofp_to_string(oh, ntohs(oh->length), 2);
+        char *s = ofp_to_string(oh, ntohs(oh->length), NULL, 2);
         vlog(&this_module, level, "%s: %s", title, s);
         free(s);
     }
@@ -687,9 +687,9 @@ ovn_flow_to_string(const struct ovn_flow *f)
     struct ds s = DS_EMPTY_INITIALIZER;
     ds_put_format(&s, "table_id=%"PRIu8", ", f->table_id);
     ds_put_format(&s, "priority=%"PRIu16", ", f->priority);
-    match_format(&f->match, &s, OFP_DEFAULT_PRIORITY);
+    match_format(&f->match, NULL, &s, OFP_DEFAULT_PRIORITY);
     ds_put_cstr(&s, ", actions=");
-    ofpacts_format(f->ofpacts, f->ofpacts_len, &s);
+    ofpacts_format(f->ofpacts, f->ofpacts_len, NULL, &s);
     return ds_steal_cstr(&s);
 }
 
@@ -879,7 +879,7 @@ ofctrl_put(struct hmap *flow_table, struct shash *pending_ct_zones,
                           desired->group_id, ds_cstr(&desired->group));
 
             error = parse_ofp_group_mod_str(&gm, OFPGC11_ADD,
-                                            ds_cstr(&group_string),
+                                            ds_cstr(&group_string), NULL,
                                             &usable_protocols);
             if (!error) {
                 add_group_mod(&gm, &msgs);
@@ -979,7 +979,7 @@ ofctrl_put(struct hmap *flow_table, struct shash *pending_ct_zones,
             ds_put_format(&group_string, "group_id=%u", installed->group_id);
 
             error = parse_ofp_group_mod_str(&gm, OFPGC11_DELETE,
-                                            ds_cstr(&group_string),
+                                            ds_cstr(&group_string), NULL,
                                             &usable_protocols);
             if (!error) {
                 add_group_mod(&gm, &msgs);
diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c
index 9ad413376736..a1f4c2530480 100644
--- a/ovn/controller/pinctrl.c
+++ b/ovn/controller/pinctrl.c
@@ -995,7 +995,7 @@ pinctrl_recv(const struct ofp_header *oh, enum ofptype type,
         if (VLOG_IS_DBG_ENABLED()) {
             static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 300);
 
-            char *s = ofp_to_string(oh, ntohs(oh->length), 2);
+            char *s = ofp_to_string(oh, ntohs(oh->length), NULL, 2);
 
             VLOG_DBG_RL(&rl, "OpenFlow packet ignored: %s", s);
             free(s);
diff --git a/ovn/lib/expr.c b/ovn/lib/expr.c
index d57fac22d6d8..ba1fb03bf995 100644
--- a/ovn/lib/expr.c
+++ b/ovn/lib/expr.c
@@ -2901,7 +2901,7 @@ expr_matches_print(const struct hmap *matches, FILE *stream)
 
     const struct expr_match *m;
     HMAP_FOR_EACH (m, hmap_node, matches) {
-        char *s = match_to_string(&m->match, OFP_DEFAULT_PRIORITY);
+        char *s = match_to_string(&m->match, NULL, OFP_DEFAULT_PRIORITY);
         fputs(s, stream);
         free(s);
 
diff --git a/ovn/utilities/ovn-sbctl.c b/ovn/utilities/ovn-sbctl.c
index 0142062fd13f..2e39e3471b5a 100644
--- a/ovn/utilities/ovn-sbctl.c
+++ b/ovn/utilities/ovn-sbctl.c
@@ -792,17 +792,17 @@ sbctl_dump_openflow(struct vconn *vconn, const struct uuid *uuid, bool stats)
 
             ds_clear(&s);
             if (stats) {
-                ofp_print_flow_stats(&s, fs);
+                ofp_print_flow_stats(&s, fs, NULL);
             } else {
                 ds_put_format(&s, " %stable=%s%"PRIu8" ",
                               colors.special, colors.end, fs->table_id);
-                match_format(&fs->match, &s, OFP_DEFAULT_PRIORITY);
+                match_format(&fs->match, NULL, &s, OFP_DEFAULT_PRIORITY);
                 if (ds_last(&s) != ' ') {
                     ds_put_char(&s, ' ');
                 }
 
                 ds_put_format(&s, "%sactions=%s", colors.actions, colors.end);
-                ofpacts_format(fs->ofpacts, fs->ofpacts_len, &s);
+                ofpacts_format(fs->ofpacts, fs->ofpacts_len, NULL, &s);
             }
             printf("   %s\n", ds_cstr(&s));
         }
diff --git a/ovn/utilities/ovn-trace.c b/ovn/utilities/ovn-trace.c
index a9970af14b82..23210d6b591a 100644
--- a/ovn/utilities/ovn-trace.c
+++ b/ovn/utilities/ovn-trace.c
@@ -1295,7 +1295,7 @@ summarize_move(const struct mf_subfield *rsrc,
         if (rsrc->ofs == 0 && rsrc->n_bits >= rsrc->field->n_bits) {
             union mf_value value;
             mf_get_value(rsrc->field, uflow, &value);
-            mf_format(rsrc->field, &value, NULL, &s);
+            mf_format(rsrc->field, &value, NULL, NULL, &s);
         } else {
             union mf_subvalue cst;
             mf_read_subfield(rsrc, uflow, &cst);
@@ -1878,7 +1878,7 @@ trace_openflow(const struct ovntrace_flow *f, struct ovs_list *super)
         struct ds s = DS_EMPTY_INITIALIZER;
         for (size_t i = 0; i < n_fses; i++) {
             ds_clear(&s);
-            ofp_print_flow_stats(&s, &fses[i]);
+            ofp_print_flow_stats(&s, &fses[i], NULL);
 
             /* ofp_print_flow_stats() indents its output with a space.
              * Omit it. */
@@ -1971,7 +1971,7 @@ trace(const char *dp_s, const char *flow_s)
     struct ds output = DS_EMPTY_INITIALIZER;
 
     ds_put_cstr(&output, "# ");
-    flow_format(&output, &uflow);
+    flow_format(&output, &uflow, NULL);
     ds_put_char(&output, '\n');
 
     if (ovs) {
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index e52ab32b9e5f..d7f2d2fcb7a3 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -219,7 +219,7 @@ table=1 in_port=2 priority=1500 icmp actions=output(17),resubmit(,2)
 table=1 in_port=3 priority=1500 icmp actions=output(14),resubmit(,2)
 ])
 AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
-AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=1,nw_tos=0,nw_ttl=128,icmp_type=8,icmp_code=0'], [0], [stdout])
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=p1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,dl_type=0x0800,nw_src=192.168.0.1,nw_dst=192.168.0.2,nw_proto=1,nw_tos=0,nw_ttl=128,icmp_type=8,icmp_code=0'], [0], [stdout])
 AT_CHECK([tail -1 stdout], [0],
   [Datapath actions: 10,11,12,13,14,15,16,17,18,19,20,21
 ])
diff --git a/tests/ofproto.at b/tests/ofproto.at
index 5431f4e8d3c6..3ce5606ad7ca 100644
--- a/tests/ofproto.at
+++ b/tests/ofproto.at
@@ -2099,9 +2099,9 @@ flow in_port=6 actions=group:1
 packet-out in_port=6, packet=0001020304050010203040501234 actions=table
 flow in_port=7 actions=group:2
 ])
-AT_CHECK([ovs-ofctl bundle br0 bundle.txt 2>&1 | sed '/talking to/,$d'], [], [dnl
-Error OFPBAC_BAD_OUT_GROUP for: OFPT_FLOW_MOD (OF1.4) (xid=0x5): ADD in_port=7 actions=group:2
-Error OFPBFC_MSG_FAILED for: OFPT_BUNDLE_CONTROL (OF1.4) (xid=0x8):
+AT_CHECK([ovs-ofctl bundle br0 bundle.txt 2>&1 | sed '/talking to/,$d' | strip_xids], [], [dnl
+Error OFPBAC_BAD_OUT_GROUP for: OFPT_FLOW_MOD (OF1.4): ADD in_port=7 actions=group:2
+Error OFPBFC_MSG_FAILED for: OFPT_BUNDLE_CONTROL (OF1.4):
  bundle_id=0 type=COMMIT_REQUEST flags=atomic ordered
 ])
 
@@ -2112,7 +2112,7 @@ OFPST_FLOW reply (OF1.4):
 ])
 
 # Verify that the packet-in was NOT received via controller action.
-AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log], [0], [])
+AT_CHECK([strip_xids < monitor.log], [0], [])
 
 OVS_VSWITCHD_STOP
 AT_CLEANUP
@@ -2140,10 +2140,10 @@ flow in_port=6 actions=group:1
 packet-out in_port=6, packet=0001020304050010203040501234 actions=table
 packet-out in_port=6, packet=0001020304050010203040501234 actions=group:2
 ])
-AT_CHECK([ovs-ofctl bundle br0 bundle.txt 2>&1 | sed '/talking to/,$d'], [], [dnl
-Error OFPBAC_BAD_OUT_GROUP for: OFPT_PACKET_OUT (OF1.4) (xid=0x5): in_port=6 actions=group:2 data_len=14
+AT_CHECK([ovs-ofctl bundle br0 bundle.txt 2>&1 | sed '/talking to/,$d' | strip_xids], [], [dnl
+Error OFPBAC_BAD_OUT_GROUP for: OFPT_PACKET_OUT (OF1.4): in_port=6 actions=group:2 data_len=14
 vlan_tci=0x0000,dl_src=00:10:20:30:40:50,dl_dst=00:01:02:03:04:05,dl_type=0x1234
-Error OFPBFC_MSG_FAILED for: OFPT_BUNDLE_CONTROL (OF1.4) (xid=0x8):
+Error OFPBFC_MSG_FAILED for: OFPT_BUNDLE_CONTROL (OF1.4):
  bundle_id=0 type=COMMIT_REQUEST flags=atomic ordered
 ])
 
@@ -2154,7 +2154,7 @@ OFPST_FLOW reply (OF1.4):
 ])
 
 # Verify that the packet-in was NOT received via controller action.
-AT_CHECK([sed 's/ (xid=0x[[1-9a-fA-F]][[0-9a-fA-F]]*)//' monitor.log], [0], [])
+AT_CHECK([strip_xids < monitor.log], [0], [])
 
 OVS_VSWITCHD_STOP
 AT_CLEANUP
@@ -2227,7 +2227,7 @@ OVS_VSWITCHD_START([set-controller br0 tcp:240.0.0.1:6653])
 for i in 1 2 3 4 5; do ovs-appctl time/warp 1000; done
 
 # Check that no hidden flows are visible in OpenFlow.
-AT_CHECK([ovs-ofctl dump-flows br0], [0], [NXST_FLOW reply (xid=0x4):
+AT_CHECK([ovs-ofctl dump-flows br0 | strip_xids], [0], [NXST_FLOW reply:
 ])
 
 # Check that some hidden flows related to 240.0.0.1 are actually in table 0.
@@ -2239,7 +2239,7 @@ AT_CHECK([test `grep '240\.0\.0\.1' stdout | grep -v table_id= | wc -l` -gt 0])
 
 # Check that dump-tables doesn't count the hidden flows.
 head_table() {
-    printf 'OFPST_TABLE reply (xid=0x2):
+    printf 'OFPST_TABLE reply:
   table 0 ("%s"):
     active=0, lookup=0, matched=0
     max_entries=1000000
@@ -2265,7 +2265,7 @@ ditto() {
     done
 }
 (head_table classifier; ditto 1 253) > expout
-AT_CHECK([ovs-ofctl dump-tables br0], [0], [expout])
+AT_CHECK([ovs-ofctl dump-tables br0 | strip_xids], [0], [expout])
 OVS_VSWITCHD_STOP(["/240\.0\.0\.1/d"])
 AT_CLEANUP
 
@@ -4910,7 +4910,7 @@ OVS_VSWITCHD_START
 
 AT_CHECK([ovs-appctl vlog/set vconn:dbg])
 
-AT_CHECK([ovs-ofctl del-flows br0])
+AT_CHECK([ovs-ofctl --no-names del-flows br0])
 
 AT_DATA([flows.txt], [dnl
 add idle_timeout=50 in_port=2 dl_src=00:66:77:88:99:aa actions=1
@@ -4924,7 +4924,7 @@ add idle_timeout=70 in_port=2 dl_src=00:88:99:aa:bb:cc actions=7
 delete in_port=2 dl_src=00:88:99:aa:bb:cc
 ])
 
-AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt])
+AT_CHECK([ovs-ofctl --no-names --bundle add-flows br0 flows.txt])
 
 AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
  idle_timeout=50, in_port=2,dl_src=00:66:77:88:99:aa actions=output:5
@@ -4937,7 +4937,7 @@ modify actions=drop
 modify_strict in_port=2 dl_src=00:77:88:99:aa:bb actions=7
 ])
 
-AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt])
+AT_CHECK([ovs-ofctl --no-names --bundle add-flows br0 flows.txt])
 
 AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
  idle_timeout=50, in_port=2,dl_src=00:66:77:88:99:aa actions=drop
@@ -4952,7 +4952,7 @@ delete_strict in_port=2 dl_src=00:66:77:88:99:aa
 add in_port=2 dl_src=00:66:77:88:99:aa actions=drop
 ])
 
-AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt])
+AT_CHECK([ovs-ofctl --no-names --bundle add-flows br0 flows.txt])
 
 AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
  idle_timeout=60, in_port=2,dl_src=00:77:88:99:aa:bb actions=output:8
@@ -5139,9 +5139,9 @@ delete in_port=2 dl_src=00:88:99:aa:bb:cc
 add table=254 actions=drop
 ])
 
-AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt 2>&1 | sed '/talking to/,$d'],
+AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt 2>&1 | sed '/talking to/,$d' | strip_xids],
 [0], [dnl
-Error OFPBRC_EPERM for: OFPT_FLOW_MOD (OF1.4) (xid=0xb): ADD table:254 actions=drop
+Error OFPBRC_EPERM for: OFPT_FLOW_MOD (OF1.4): ADD table:254 actions=drop
 ])
 
 AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
@@ -5463,7 +5463,7 @@ OVS_VSWITCHD_START
 
 AT_CHECK([ovs-appctl vlog/set vconn:dbg])
 
-AT_CHECK([ovs-ofctl del-flows br0])
+AT_CHECK([ovs-ofctl --no-names del-flows br0])
 
 AT_DATA([flows.txt], [dnl
 add idle_timeout=50 in_port=2 dl_src=00:66:77:88:99:aa actions=1
@@ -5477,7 +5477,7 @@ add idle_timeout=70 in_port=2 dl_src=00:88:99:aa:bb:cc actions=7
 delete in_port=2 dl_src=00:88:99:aa:bb:cc
 ])
 
-AT_CHECK([ovs-ofctl -O OpenFlow13 --bundle add-flows br0 flows.txt])
+AT_CHECK([ovs-ofctl -O OpenFlow13 --no-names --bundle add-flows br0 flows.txt])
 
 AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
  idle_timeout=50, in_port=2,dl_src=00:66:77:88:99:aa actions=output:5
@@ -5490,7 +5490,7 @@ modify actions=drop
 modify_strict in_port=2 dl_src=00:77:88:99:aa:bb actions=7
 ])
 
-AT_CHECK([ovs-ofctl -O OpenFlow13 --bundle add-flows br0 flows.txt])
+AT_CHECK([ovs-ofctl -O OpenFlow13 --no-names --bundle add-flows br0 flows.txt])
 
 AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
  idle_timeout=50, in_port=2,dl_src=00:66:77:88:99:aa actions=drop
@@ -5505,7 +5505,7 @@ delete_strict in_port=2 dl_src=00:66:77:88:99:aa
 add in_port=2 dl_src=00:66:77:88:99:aa actions=drop
 ])
 
-AT_CHECK([ovs-ofctl -O OpenFlow13 --bundle add-flows br0 flows.txt])
+AT_CHECK([ovs-ofctl -O OpenFlow13 --bundle --no-names add-flows br0 flows.txt])
 
 AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
  idle_timeout=60, in_port=2,dl_src=00:77:88:99:aa:bb actions=output:8
@@ -5685,9 +5685,9 @@ delete in_port=2 dl_src=00:88:99:aa:bb:cc
 add table=254 actions=drop
 ])
 
-AT_CHECK([ovs-ofctl -O OpenFlow13 --bundle add-flows br0 flows.txt 2>&1 | sed '/talking to/,$d'],
+AT_CHECK([ovs-ofctl -O OpenFlow13 --bundle add-flows br0 flows.txt 2>&1 | sed '/talking to/,$d' | strip_xids],
 [0], [dnl
-Error OFPBRC_EPERM for: OFPT_FLOW_MOD (OF1.3) (xid=0xb): ADD table:254 actions=drop
+Error OFPBRC_EPERM for: OFPT_FLOW_MOD (OF1.3): ADD table:254 actions=drop
 ])
 
 AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
@@ -5713,10 +5713,10 @@ add idle_timeout=60 in_port=2 dl_src=00:77:88:99:aa:bb actions=group:2
 add idle_timeout=70 in_port=2 dl_src=00:88:99:aa:bb:cc actions=group:3
 ])
 
-AT_CHECK([ovs-ofctl -O OpenFlow13 --bundle add-flows br0 flows.txt 2>&1 | sed '/talking to/,$d'],
+AT_CHECK([ovs-ofctl -O OpenFlow13 --bundle add-flows br0 flows.txt 2>&1 | sed '/talking to/,$d' | strip_xids],
 [0], [dnl
-Error OFPBAC_BAD_OUT_GROUP for: OFPT_FLOW_MOD (OF1.3) (xid=0x2): ADD in_port=2,dl_src=00:66:77:88:99:aa idle:50 actions=group:1
-Error OFPBFC_MSG_FAILED for: ONFT_BUNDLE_CONTROL (OF1.3) (xid=0x7):
+Error OFPBAC_BAD_OUT_GROUP for: OFPT_FLOW_MOD (OF1.3): ADD in_port=2,dl_src=00:66:77:88:99:aa idle:50 actions=group:1
+Error OFPBFC_MSG_FAILED for: ONFT_BUNDLE_CONTROL (OF1.3):
  bundle_id=0 type=COMMIT_REQUEST flags=atomic ordered
 ])
 
@@ -5783,8 +5783,8 @@ dnl Check the length of tun_metadata0 in the replied OXM header.
 dnl Ignore the first 0x50 bytes of hex dump from the reply msg since the NXM
 dnl header that describes the tunnel metadata starts at offset 0x50.
 AT_CHECK([ovs-ofctl dump-flows br0 -mmmm], [0], [stdout])
-AT_CHECK([sed -e 's/duration=[[0-9.]]*s/duration=?s/' -e 's/idle_age=[[0-9]]*/idle_age=?/' -e '/^000000[[0-4]]0 / d' stdout], [0], [dnl
-NXST_FLOW reply (xid=0x4):
+AT_CHECK([sed -e 's/duration=[[0-9.]]*s/duration=?s/' -e 's/idle_age=[[0-9]]*/idle_age=?/' -e '/^000000[[0-4]]0 / d' stdout | strip_xids], [0], [dnl
+NXST_FLOW reply:
  cookie=0x0, duration=?s, table=0, n_packets=0, n_bytes=0, idle_age=?, in_port=1 actions=move:NXM_NX_TUN_METADATA0[[0..31]]->NXM_NX_REG0[[]]
 00000050  ff ff 00 18 00 00 23 20-00 06 00 20 00 00 00 00 |......# ... ....|
 00000060  00 01 50 04 00 01 00 04-                        |..P.....        |
diff --git a/tests/ovs-ofctl.at b/tests/ovs-ofctl.at
index c45fc23ca3cb..6afe8f766627 100644
--- a/tests/ovs-ofctl.at
+++ b/tests/ovs-ofctl.at
@@ -373,9 +373,12 @@ done
 AT_CLEANUP
 
 AT_SETUP([ovs-ofctl action inconsistency (OpenFlow 1.1)])
+OVS_VSWITCHD_START
+add_of_ports br0 1 2 3
 AT_CHECK([ovs-ofctl --protocols OpenFlow11 add-flow br0 'ip actions=mod_tp_dst:1234'
 ], [1], [stdout], [ovs-ofctl: none of the usable flow formats (OpenFlow10,NXM) is among the allowed flow formats (OpenFlow11)
 ])
+OVS_VSWITCHD_STOP
 AT_CLEANUP
 
 AT_SETUP([ovs-ofctl parse-flows (skb_priority)])
@@ -484,8 +487,8 @@ NXT_FLOW_MOD: ADD table:255 ip actions=ct(commit,exec(load:0x1->NXM_NX_CT_LABEL[
 NXT_FLOW_MOD: ADD table:255 ct_state=+trk,ct_label=0x1234567890abcdef12345678,ip actions=ct(commit)
 NXT_FLOW_MOD: ADD table:255 actions=output(port=123,max_len=100)
 NXT_FLOW_MOD: ADD table:255 actions=output(port=100,max_len=123)
-NXT_FLOW_MOD: ADD table:255 actions=output(port=65534,max_len=123)
-NXT_FLOW_MOD: ADD table:255 actions=output(port=65528,max_len=123)
+NXT_FLOW_MOD: ADD table:255 actions=output(port=LOCAL,max_len=123)
+NXT_FLOW_MOD: ADD table:255 actions=output(port=IN_PORT,max_len=123)
 ]])
 AT_CLEANUP
 
@@ -2595,12 +2598,14 @@ AT_CLEANUP
 dnl Check that "-F openflow10" rejects a flow_mod with unsupported features,
 dnl such as tunnels and metadata.
 AT_SETUP([ovs-ofctl -F option and NXM features])
-AT_CHECK([ovs-ofctl -F openflow10 add-flow dummy tun_id=123,actions=drop],
+OVS_VSWITCHD_START
+AT_CHECK([ovs-ofctl -F openflow10 add-flow br0 tun_id=123,actions=drop],
   [1], [], [ovs-ofctl: none of the usable flow formats (NXM,OXM) is among the allowed flow formats (OpenFlow10)
 ])
-AT_CHECK([ovs-ofctl -F openflow10 add-flow dummy metadata=123,actions=drop],
+AT_CHECK([ovs-ofctl -F openflow10 add-flow br0 metadata=123,actions=drop],
   [1], [], [ovs-ofctl: none of the usable flow formats (NXM,OXM,OpenFlow11) is among the allowed flow formats (OpenFlow10)
 ])
+OVS_VSWITCHD_STOP
 AT_CLEANUP
 
 dnl Check that "-F nxm" really forces add-flow to use the NXM flow format.
@@ -2757,6 +2762,43 @@ AT_CHECK(
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
+AT_SETUP([ovs-ofctl dump-flows --names])
+AT_KEYWORDS([port names])
+OVS_VSWITCHD_START([\
+    -- add-port br0 xyzzy -- set Interface xyzzy type=dummy -- \
+    -- add-port br0 x-y -- set Interface x-y type=dummy -- \
+    -- add-port br0 abc123 -- set Interface abc123 type=dummy -- \
+    -- add-port br0 reallyverylongportname -- set Interface reallyverylongportname type=dummy -- \
+    -- add-port br0 conflictinglongportname1 -- set Interface conflictinglongportname1 type=dummy -- \
+    -- add-port br0 conflictinglongportname2 -- set Interface conflictinglongportname2 type=dummy])
+
+# These plain port names should be accepted.
+AT_CHECK([ovs-ofctl add-flow br0 in_port=xyzzy,actions=x-y,abc123])
+
+# reallyverylongportname is accepted truncated, but not in full.
+AT_CHECK([ovs-ofctl add-flow br0 in_port=reallyverylongp,actions=drop])
+AT_CHECK([ovs-ofctl add-flow br0 in_port=reallyverylongportname,actions=drop],
+  [1], [], [ovs-ofctl: reallyverylongportname: invalid or unknown port for in_port
+])
+
+# conflictinglongportname1 and 2 can't be accepted even truncated, since
+# they conflict when truncated.
+AT_CHECK([ovs-ofctl add-flow br0 in_port=conflictinglongportname1,actions=drop], [1], [], [ovs-ofctl: conflictinglongportname1: invalid or unknown port for in_port
+])
+AT_CHECK([ovs-ofctl add-flow br0 in_port=conflictinglongportname2,actions=drop], [1], [], [ovs-ofctl: conflictinglongportname2: invalid or unknown port for in_port
+])
+AT_CHECK([ovs-ofctl add-flow br0 in_port=conflictinglong,actions=drop], [1], [], [ovs-ofctl: conflictinglong: invalid or unknown port for in_port
+])
+
+# Show that the port names get displayed properly and that port names that
+# aren't alphanumeric get quoted.
+AT_CHECK([ovs-ofctl --names dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ in_port=reallyverylongp actions=drop
+ in_port=xyzzy actions=output:"x-y",output:abc123
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
 AT_SETUP([ovs-ofctl diff-flows])
 OVS_VSWITCHD_START
 
@@ -2995,15 +3037,15 @@ AT_CHECK([ovs-appctl vlog/set vconn:dbg])
 
 dnl Add flows to br0 with importance via OF1.4+, using an OF1.4+ bundle. For more details refer "ovs-ofctl rule with importance" test case.
 for i in 1 2 3 4 5 6 7 8; do echo "table=$i,dl_vlan=$i,importance=$i,actions=drop"; done > add-flows.txt
-AT_CHECK([ovs-ofctl --bundle add-flows br0 add-flows.txt])
+AT_CHECK([ovs-ofctl --bundle --no-names add-flows br0 add-flows.txt])
 
 dnl Replace some flows in the bridge.
 for i in 1 3 5 7; do echo " table=$i, importance=`expr $i + 10`, dl_vlan=$i actions=drop"; done > replace-flows.txt
-AT_CHECK([ovs-ofctl --bundle replace-flows br0 replace-flows.txt])
+AT_CHECK([ovs-ofctl --bundle --no-names replace-flows br0 replace-flows.txt])
 
 dnl Dump them and compare the dump flows output against the expected output.
 cat replace-flows.txt > expout
-AT_CHECK([ovs-ofctl -O OpenFlow14 dump-flows br0 | ofctl_strip | sed '/OFPST_FLOW/d' | sort],
+AT_CHECK([ovs-ofctl -O OpenFlow14 --no-names dump-flows br0 | ofctl_strip | sed '/OFPST_FLOW/d' | sort],
   [0], [expout])
 
 dnl Check logs for OpenFlow trace
diff --git a/tests/test-bundle.c b/tests/test-bundle.c
index 88949be39ff4..af25432002eb 100644
--- a/tests/test-bundle.c
+++ b/tests/test-bundle.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, 2012, 2013, 2014 Nicira, Inc.
+/* Copyright (c) 2011, 2012, 2013, 2014, 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.
@@ -71,7 +71,7 @@ parse_bundle_actions(char *actions)
     char *error;
 
     ofpbuf_init(&ofpacts, 0);
-    error = bundle_parse_load(actions, &ofpacts);
+    error = bundle_parse_load(actions, NULL, &ofpacts);
     if (error) {
         ovs_fatal(0, "%s", error);
     }
diff --git a/tests/test-flows.c b/tests/test-flows.c
index 067f36a49bc1..d7fa798df17d 100644
--- a/tests/test-flows.c
+++ b/tests/test-flows.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 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.
@@ -78,14 +78,14 @@ test_flows_main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
         ofputil_match_to_ofp10_match(&match, &extracted_match);
 
         if (memcmp(&expected_match, &extracted_match, sizeof expected_match)) {
-            char *exp_s = ofp10_match_to_string(&expected_match, 2);
-            char *got_s = ofp10_match_to_string(&extracted_match, 2);
+            char *exp_s = ofp10_match_to_string(&expected_match, NULL, 2);
+            char *got_s = ofp10_match_to_string(&extracted_match, NULL, 2);
             errors++;
             printf("mismatch on packet #%d (1-based).\n", n);
             printf("Packet:\n");
             ofp_print_packet(stdout, dp_packet_data(packet), dp_packet_size(packet), htonl(PT_ETH));
             ovs_hex_dump(stdout, dp_packet_data(packet), dp_packet_size(packet), 0, true);
-            match_print(&match);
+            match_print(&match, NULL);
             printf("Expected flow:\n%s\n", exp_s);
             printf("Actually extracted flow:\n%s\n", got_s);
             ovs_hex_dump(stdout, &expected_match, sizeof expected_match, 0, false);
diff --git a/tests/test-ovn.c b/tests/test-ovn.c
index 18860aeee661..3980b655e264 100644
--- a/tests/test-ovn.c
+++ b/tests/test-ovn.c
@@ -1254,7 +1254,7 @@ test_parse_actions(struct ovs_cmdl_context *ctx OVS_UNUSED)
             ofpbuf_init(&ofpacts, 0);
             ovnacts_encode(ovnacts.data, ovnacts.size, &ep, &ofpacts);
             struct ds ofpacts_s = DS_EMPTY_INITIALIZER;
-            ofpacts_format(ofpacts.data, ofpacts.size, &ofpacts_s);
+            ofpacts_format(ofpacts.data, ofpacts.size, NULL, &ofpacts_s);
             printf("    encodes as %s\n", ds_cstr(&ofpacts_s));
             ds_destroy(&ofpacts_s);
             ofpbuf_uninit(&ofpacts);
diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in
index a7e805587186..54564cfb7c28 100644
--- a/utilities/ovs-ofctl.8.in
+++ b/utilities/ovs-ofctl.8.in
@@ -128,7 +128,8 @@ versions of OpenFlow always dump all ports.
 .
 .IP "\fBmod\-port \fIswitch\fR \fIport\fR \fIaction\fR"
 Modify characteristics of port \fBport\fR in \fIswitch\fR.  \fIport\fR
-may be an OpenFlow port number or name or the keyword \fBLOCAL\fR (the
+may be an OpenFlow port number or name (unless \fB\-\-no\-names\fR is
+specified) or the keyword \fBLOCAL\fR (the
 preferred way to refer to the OpenFlow local port).  The \fIaction\fR
 may be any one of the following:
 .
@@ -2187,6 +2188,35 @@ depending on its configuration.
 \fB\-\-strict\fR
 Uses strict matching when running flow modification commands.
 .
+.IP "\fB\-\-names\fR"
+.IQ "\fB\-\-no\-names\fR"
+Every OpenFlow port has a name and a number.  By default,
+\fBovs\-ofctl\fR commands accept both port names and numbers, and they
+display port names if \fBovs\-ofctl\fR is running on an interactive
+console, port numbers otherwise.  With \fB\-\-names\fR,
+\fBovs\-ofctl\fR commands both accept and display port names; with
+\fB\-\-no\-names\fR, commands neither accept nor display port names.
+.IP
+If a port name contains special characters or might be confused with a
+keyword within a flow, it may be enclosed in double quotes (escaped
+from the shell).  If necessary, JSON-style escape sequences may be
+used inside quotes, as specified in RFC 7159.  When it displays port
+names, \fBovs\-ofctl\fR quotes any name that does not start with a
+letter followed by letters or digits.
+.IP
+These options are new in Open vSwitch 2.8.  Earlier versions always
+behaved as if \fB\-\-no\-names\fR were specified.
+.IP
+Open vSwitch does not place its own limit on the length of port names,
+but OpenFlow 1.0 to 1.5 limit port names to 15 bytes and OpenFlow 1.6
+limits them to 63 bytes.  Because \fRovs\-ofctl\fR uses OpenFlow to
+retrieve the mapping between port names and numbers, names longer than
+this limit will be truncated for both display and acceptance.
+Truncation can also cause long names that are different to appear to
+be the same; when a switch has two ports with the same (truncated)
+name, \fBovs\-ofctl\fR refuses to display or accept the name, using
+the number instead.
+.
 .IP "\fB\-\-read-only\fR"
 Do not execute read/write commands.
 .
diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
index 1a5e2345b7d4..050c491e792a 100644
--- a/utilities/ovs-ofctl.c
+++ b/utilities/ovs-ofctl.c
@@ -53,6 +53,7 @@
 #include "openvswitch/ofp-util.h"
 #include "openvswitch/ofp-parse.h"
 #include "openvswitch/ofpbuf.h"
+#include "openvswitch/shash.h"
 #include "openvswitch/vconn.h"
 #include "openvswitch/vlog.h"
 #include "packets.h"
@@ -124,6 +125,16 @@ struct sort_criterion {
 static struct sort_criterion *criteria;
 static size_t n_criteria, allocated_criteria;
 
+/* --names, --no-names: Show port names in output and accept port numbers in
+ * input.  (When neither is specified, the default is to accept port numbers
+ * but, for backward compatibility, not to show them unless this is an
+ * interactive console session.)  */
+static int use_port_names = -1;
+static const struct ofputil_port_map *ports_to_accept(const char *vconn_name);
+static const struct ofputil_port_map *ports_to_show(const char *vconn_name);
+static bool should_accept_ports(void);
+static bool should_show_ports(void);
+
 static const struct ovs_cmdl_command *get_all_commands(void);
 
 OVS_NO_RETURN static void usage(void);
@@ -199,6 +210,8 @@ parse_options(int argc, char *argv[])
         {"timestamp", no_argument, NULL, OPT_TIMESTAMP},
         {"sort", optional_argument, NULL, OPT_SORT},
         {"rsort", optional_argument, NULL, OPT_RSORT},
+        {"names", no_argument, &use_port_names, 1},
+        {"no-names", no_argument, &use_port_names, 0},
         {"unixctl",     required_argument, NULL, OPT_UNIXCTL},
         {"help", no_argument, NULL, 'h'},
         {"option", no_argument, NULL, 'o'},
@@ -347,6 +360,9 @@ parse_options(int argc, char *argv[])
         case '?':
             exit(EXIT_FAILURE);
 
+        case 0:
+            break;
+
         default:
             abort();
         }
@@ -472,6 +488,7 @@ usage(void)
            "  -t, --timeout=SECS          give up after SECS seconds\n"
            "  --sort[=field]              sort in ascending order\n"
            "  --rsort[=field]             sort in descending order\n"
+           "  --names                     show port names instead of numbers\n"
            "  --unixctl=SOCKET            set control socket name\n"
            "  --color[=always|never|auto] control use of color in output\n"
            "  -h, --help                  display this help message\n"
@@ -613,7 +630,8 @@ dump_transaction(struct vconn *vconn, struct ofpbuf *request)
             if (send_xid == recv_xid) {
                 enum ofpraw raw;
 
-                ofp_print(stdout, reply->data, reply->size, verbosity + 1);
+                ofp_print(stdout, reply->data, reply->size,
+                          ports_to_show(vconn_get_name(vconn)), verbosity + 1);
 
                 ofpraw_decode(&raw, reply->data);
                 if (ofptype_from_ofpraw(raw) == OFPTYPE_ERROR) {
@@ -622,8 +640,10 @@ dump_transaction(struct vconn *vconn, struct ofpbuf *request)
                     done = !ofpmp_more(reply->data);
                 } else {
                     ovs_fatal(0, "received bad reply: %s",
-                              ofp_to_string(reply->data, reply->size,
-                                            verbosity + 1));
+                              ofp_to_string(
+                                  reply->data, reply->size,
+                                  ports_to_show(vconn_get_name(vconn)),
+                                  verbosity + 1));
                 }
             } else {
                 VLOG_DBG("received reply with xid %08"PRIx32" "
@@ -636,7 +656,8 @@ dump_transaction(struct vconn *vconn, struct ofpbuf *request)
 
         run(vconn_transact(vconn, request, &reply), "talking to %s",
             vconn_get_name(vconn));
-        ofp_print(stdout, reply->data, reply->size, verbosity + 1);
+        ofp_print(stdout, reply->data, reply->size,
+                  ports_to_show(vconn_get_name(vconn)), verbosity + 1);
         ofpbuf_delete(reply);
     }
 }
@@ -666,7 +687,8 @@ transact_multiple_noreply(struct vconn *vconn, struct ovs_list *requests)
     run(vconn_transact_multiple_noreply(vconn, requests, &reply),
         "talking to %s", vconn_get_name(vconn));
     if (reply) {
-        ofp_print(stderr, reply->data, reply->size, verbosity + 2);
+        ofp_print(stderr, reply->data, reply->size,
+                  ports_to_show(vconn_get_name(vconn)), verbosity + 2);
         exit(1);
     }
     ofpbuf_delete(reply);
@@ -674,7 +696,8 @@ transact_multiple_noreply(struct vconn *vconn, struct ovs_list *requests)
 
 /* Frees the error messages as they are printed. */
 static void
-bundle_print_errors(struct ovs_list *errors, struct ovs_list *requests)
+bundle_print_errors(struct ovs_list *errors, struct ovs_list *requests,
+                    const char *vconn_name)
 {
     struct vconn_bundle_error *error, *next;
     struct ofpbuf *bmsg;
@@ -708,7 +731,8 @@ bundle_print_errors(struct ovs_list *errors, struct ovs_list *requests)
                 }
             }
             fprintf(stderr, "Error %s for: ", ofperr_get_name(ofperr));
-            ofp_print(stderr, ofp_msg, msg_len, verbosity + 1);
+            ofp_print(stderr, ofp_msg, msg_len, ports_to_show(vconn_name),
+                      verbosity + 1);
         }
         ofpbuf_uninit(&payload);
         free(error);
@@ -722,7 +746,7 @@ bundle_transact(struct vconn *vconn, struct ovs_list *requests, uint16_t flags)
     struct ovs_list errors;
     int retval = vconn_bundle_transact(vconn, requests, flags, &errors);
 
-    bundle_print_errors(&errors, requests);
+    bundle_print_errors(&errors, requests, vconn_get_name(vconn));
 
     if (retval) {
         ovs_fatal(retval, "talking to %s", vconn_get_name(vconn));
@@ -788,7 +812,7 @@ ofctl_show(struct ovs_cmdl_context *ctx)
     run(vconn_transact(vconn, request, &reply), "talking to %s", vconn_name);
 
     has_ports = ofputil_switch_features_has_ports(reply);
-    ofp_print(stdout, reply->data, reply->size, verbosity + 1);
+    ofp_print(stdout, reply->data, reply->size, NULL, verbosity + 1);
     ofpbuf_delete(reply);
 
     if (!has_ports) {
@@ -849,7 +873,8 @@ ofctl_dump_table_features(struct ovs_cmdl_context *ctx)
             if (error) {
                 ovs_fatal(0, "decode error: %s", ofperr_get_name(error));
             } else if (type == OFPTYPE_ERROR) {
-                ofp_print(stdout, reply->data, reply->size, verbosity + 1);
+                ofp_print(stdout, reply->data, reply->size, NULL,
+                          verbosity + 1);
                 done = true;
             } else if (type == OFPTYPE_TABLE_FEATURES_STATS_REPLY) {
                 done = !ofpmp_more(reply->data);
@@ -878,6 +903,7 @@ ofctl_dump_table_features(struct ovs_cmdl_context *ctx)
             } else {
                 ovs_fatal(0, "received bad reply: %s",
                           ofp_to_string(reply->data, reply->size,
+                                        ports_to_show(ctx->argv[1]),
                                         verbosity + 1));
             }
         } else {
@@ -1007,7 +1033,7 @@ port_iterator_next(struct port_iterator *pi, struct ofputil_phy_port *pp)
             } else if (retval != EOF) {
                 ovs_fatal(0, "received bad reply: %s",
                           ofp_to_string(pi->reply->data, pi->reply->size,
-                                        verbosity + 1));
+                                        NULL, verbosity + 1));
             }
         }
 
@@ -1028,7 +1054,7 @@ port_iterator_next(struct port_iterator *pi, struct ofputil_phy_port *pp)
         if (ofptype_pull(&type, pi->reply)
             || type != OFPTYPE_PORT_DESC_STATS_REPLY) {
             ovs_fatal(0, "received bad reply: %s",
-                      ofp_to_string(pi->reply->data, pi->reply->size,
+                      ofp_to_string(pi->reply->data, pi->reply->size, NULL,
                                     verbosity + 1));
         }
 
@@ -1088,21 +1114,85 @@ fetch_ofputil_phy_port(const char *vconn_name, const char *port_name,
     }
 }
 
+static const struct ofputil_port_map *
+get_port_map(const char *vconn_name)
+{
+    static struct shash port_maps = SHASH_INITIALIZER(&port_maps);
+    struct ofputil_port_map *map = shash_find_data(&port_maps, vconn_name);
+    if (!map) {
+        map = xmalloc(sizeof *map);
+        ofputil_port_map_init(map);
+        shash_add(&port_maps, vconn_name, map);
+
+        if (!strchr(vconn_name, ':') || !vconn_verify_name(vconn_name)) {
+            /* For an active vconn (which includes a vconn constructed from a
+             * bridge name), connect to it and pull down the port name-number
+             * mapping. */
+            struct vconn *vconn;
+            open_vconn(vconn_name, &vconn);
+
+            struct port_iterator pi;
+            struct ofputil_phy_port pp;
+            for (port_iterator_init(&pi, vconn);
+                 port_iterator_next(&pi, &pp); ) {
+                ofputil_port_map_put(map, pp.port_no, pp.name);
+            }
+            port_iterator_destroy(&pi);
+
+            vconn_close(vconn);
+        } else {
+            /* Don't bother with passive vconns, since it could take a long
+             * time for the remote to try to connect to us.  Don't bother with
+             * invalid vconn names either. */
+        }
+    }
+    return map;
+}
+
+static const struct ofputil_port_map *
+ports_to_accept(const char *vconn_name)
+{
+    return should_accept_ports() ? get_port_map(vconn_name) : NULL;
+}
+
+static const struct ofputil_port_map *
+ports_to_show(const char *vconn_name)
+{
+    return should_show_ports() ? get_port_map(vconn_name) : NULL;
+}
+
+/* We accept port names unless the feature is turned off explicitly. */
+static bool
+should_accept_ports(void)
+{
+    return use_port_names != 0;
+}
+
+/* We show port names only if the feature is turned on explicitly, or if we're
+ * interacting with a user on the console. */
+static bool
+should_show_ports(void)
+{
+    static int interactive = -1;
+    if (interactive == -1) {
+        interactive = isatty(STDOUT_FILENO);
+    }
+
+    return use_port_names > 0 || (use_port_names == -1 && interactive);
+}
+
 /* Returns the port number corresponding to 'port_name' (which may be a port
  * name or number) within the switch 'vconn_name'. */
 static ofp_port_t
 str_to_port_no(const char *vconn_name, const char *port_name)
 {
     ofp_port_t port_no;
-
-    if (ofputil_port_from_string(port_name, &port_no)) {
+    if (ofputil_port_from_string(port_name, NULL, &port_no) ||
+        ofputil_port_from_string(port_name, ports_to_accept(vconn_name),
+                                 &port_no)) {
         return port_no;
-    } else {
-        struct ofputil_phy_port pp;
-
-        fetch_ofputil_phy_port(vconn_name, port_name, &pp);
-        return pp.port_no;
     }
+    ovs_fatal(0, "%s: unknown port `%s'", vconn_name, port_name);
 }
 
 static bool
@@ -1121,7 +1211,7 @@ try_set_protocol(struct vconn *vconn, enum ofputil_protocol want,
         run(vconn_transact_noreply(vconn, request, &reply),
             "talking to %s", vconn_get_name(vconn));
         if (reply) {
-            char *s = ofp_to_string(reply->data, reply->size, 2);
+            char *s = ofp_to_string(reply->data, reply->size, NULL, 2);
             VLOG_DBG("%s: failed to set protocol, switch replied: %s",
                      vconn_get_name(vconn), s);
             free(s);
@@ -1153,7 +1243,7 @@ set_protocol_for_flow_dump(struct vconn *vconn,
     if (usable_protocols & allowed_protocols) {
         ovs_fatal(0, "switch does not support any of the usable flow "
                   "formats (%s)", usable_s);
-    } else {
+} else {
         char *allowed_s = ofputil_protocols_to_string(allowed_protocols);
         ovs_fatal(0, "none of the usable flow formats (%s) is among the "
                   "allowed flow formats (%s)", usable_s, allowed_s);
@@ -1165,18 +1255,21 @@ prepare_dump_flows(int argc, char *argv[], bool aggregate,
                    struct ofputil_flow_stats_request *fsr,
                    enum ofputil_protocol *protocolp)
 {
+    const char *vconn_name = argv[1];
     enum ofputil_protocol usable_protocols, protocol;
     struct vconn *vconn;
     char *error;
 
-    error = parse_ofp_flow_stats_request_str(fsr, aggregate,
-                                             argc > 2 ? argv[2] : "",
-                                             &usable_protocols);
+    const char *match = argc > 2 ? argv[2] : "";
+    const struct ofputil_port_map *port_map
+        = *match ? ports_to_accept(vconn_name) : NULL;
+    error = parse_ofp_flow_stats_request_str(fsr, aggregate, match,
+                                             port_map, &usable_protocols);
     if (error) {
         ovs_fatal(0, "%s", error);
     }
 
-    protocol = open_vconn(argv[1], &vconn);
+    protocol = open_vconn(vconn_name, &vconn);
     *protocolp = set_protocol_for_flow_dump(vconn, protocol, usable_protocols);
     return vconn;
 }
@@ -1262,7 +1355,7 @@ compare_flows(const void *afs_, const void *bfs_)
 static void
 ofctl_dump_flows(struct ovs_cmdl_context *ctx)
 {
-    if (!n_criteria) {
+    if (!n_criteria && !should_show_ports()) {
         ofctl_dump_flows__(ctx->argc, ctx->argv, false);
         return;
     } else {
@@ -1283,7 +1376,7 @@ ofctl_dump_flows(struct ovs_cmdl_context *ctx)
         struct ds s = DS_EMPTY_INITIALIZER;
         for (size_t i = 0; i < n_fses; i++) {
             ds_clear(&s);
-            ofp_print_flow_stats(&s, &fses[i]);
+            ofp_print_flow_stats(&s, &fses[i], ports_to_show(ctx->argv[1]));
             puts(ds_cstr(&s));
         }
         ds_destroy(&s);
@@ -1474,8 +1567,8 @@ ofctl_flow_mod_file(int argc OVS_UNUSED, char *argv[], int command)
          * this is backwards compatible. */
         command = -2;
     }
-    error = parse_ofp_flow_mod_file(argv[2], command, &fms, &n_fms,
-                                    &usable_protocols);
+    error = parse_ofp_flow_mod_file(argv[2], ports_to_accept(argv[1]), command,
+                                    &fms, &n_fms, &usable_protocols);
     if (error) {
         ovs_fatal(0, "%s", error);
     }
@@ -1493,7 +1586,8 @@ ofctl_flow_mod(int argc, char *argv[], uint16_t command)
         char *error;
         enum ofputil_protocol usable_protocols;
 
-        error = parse_ofp_flow_mod_str(&fm, argc > 2 ? argv[2] : "", command,
+        error = parse_ofp_flow_mod_str(&fm, argc > 2 ? argv[2] : "",
+                                       ports_to_accept(argv[1]), command,
                                        &usable_protocols);
         if (error) {
             ovs_fatal(0, "%s", error);
@@ -1543,7 +1637,7 @@ set_packet_in_format(struct vconn *vconn,
         run(vconn_transact_noreply(vconn, spif, &reply),
             "talking to %s", vconn_get_name(vconn));
         if (reply) {
-            char *s = ofp_to_string(reply->data, reply->size, 2);
+            char *s = ofp_to_string(reply->data, reply->size, NULL, 2);
             VLOG_DBG("%s: failed to set packet in format to nx_packet_in, "
                      "controller replied: %s.",
                      vconn_get_name(vconn), s);
@@ -1638,7 +1732,8 @@ ofctl_send(struct unixctl_conn *conn, int argc,
         }
 
         fprintf(stderr, "send: ");
-        ofp_print(stderr, msg->data, msg->size, verbosity);
+        ofp_print(stderr, msg->data, msg->size,
+                  ports_to_show(vconn_get_name(vconn)), verbosity);
 
         error = vconn_send_block(vconn, msg);
         if (error) {
@@ -1672,7 +1767,9 @@ unixctl_packet_out(struct unixctl_conn *conn, int OVS_UNUSED argc,
     struct ofputil_packet_out po;
     char *error_msg;
 
-    error_msg = parse_ofp_packet_out_str(&po, argv[1], &usable_protocols);
+    error_msg = parse_ofp_packet_out_str(
+        &po, argv[1], ports_to_accept(vconn_get_name(vconn)),
+        &usable_protocols);
     if (error_msg) {
         ds_put_format(&reply, "%s\n", error_msg);
         free(error_msg);
@@ -1687,7 +1784,8 @@ unixctl_packet_out(struct unixctl_conn *conn, int OVS_UNUSED argc,
     if (ok) {
         struct ofpbuf *msg = ofputil_encode_packet_out(&po, protocol);
 
-        ofp_print(stderr, msg->data, msg->size, verbosity);
+        ofp_print(stderr, msg->data, msg->size,
+                  ports_to_show(vconn_get_name(vconn)), verbosity);
 
         int error = vconn_send_block(vconn, msg);
         if (error) {
@@ -1850,7 +1948,8 @@ monitor_vconn(struct vconn *vconn, bool reply_to_echo_requests,
             }
 
             ofptype_decode(&type, b->data);
-            ofp_print(stderr, b->data, b->size, verbosity + 2);
+            ofp_print(stderr, b->data, b->size,
+                      ports_to_show(vconn_get_name(vconn)), verbosity + 2);
             fflush(stderr);
 
             switch ((int) type) {
@@ -1892,6 +1991,7 @@ monitor_vconn(struct vconn *vconn, bool reply_to_echo_requests,
 
                         fprintf(stderr, "send: ");
                         ofp_print(stderr, reply->data, reply->size,
+                                  ports_to_show(vconn_get_name(vconn)),
                                   verbosity + 2);
                         fflush(stderr);
 
@@ -1970,6 +2070,7 @@ ofctl_monitor(struct ovs_cmdl_context *ctx)
             char *error;
 
             error = parse_flow_monitor_request(&fmr, arg + 6,
+                                               ports_to_accept(ctx->argv[1]),
                                                &usable_protocols);
             if (error) {
                 ovs_fatal(0, "%s", error);
@@ -2084,7 +2185,8 @@ ofctl_packet_out(struct ovs_cmdl_context *ctx)
         int i;
 
         ofpbuf_init(&ofpacts, 64);
-        error = ofpacts_parse_actions(ctx->argv[3], &ofpacts,
+        error = ofpacts_parse_actions(ctx->argv[3],
+                                      ports_to_accept(ctx->argv[1]), &ofpacts,
                                       &usable_protocols);
         if (error) {
             ovs_fatal(0, "%s", error);
@@ -2115,7 +2217,9 @@ ofctl_packet_out(struct ovs_cmdl_context *ctx)
         vconn_close(vconn);
         ofpbuf_uninit(&ofpacts);
     } else if (ctx->argc == 3) {
-        error = parse_ofp_packet_out_str(&po, ctx->argv[2], &usable_protocols);
+        error = parse_ofp_packet_out_str(&po, ctx->argv[2],
+                                         ports_to_accept(ctx->argv[1]),
+                                         &usable_protocols);
         if (error) {
             ovs_fatal(0, "%s", error);
         }
@@ -2221,7 +2325,7 @@ fetch_table_desc(struct vconn *vconn, struct ofputil_table_mod *tm,
             if (ofptype_pull(&type, &b)
                 || type != OFPTYPE_TABLE_DESC_REPLY) {
                 ovs_fatal(0, "received bad reply: %s",
-                          ofp_to_string(reply->data, reply->size,
+                          ofp_to_string(reply->data, reply->size, NULL,
                                         verbosity + 1));
             }
             uint16_t flags = ofpmp_flags(oh);
@@ -2389,7 +2493,7 @@ ofctl_ofp_parse(struct ovs_cmdl_context *ctx)
             ovs_fatal(0, "%s: unexpected end of file mid-message", filename);
         }
 
-        ofp_print(stdout, b.data, b.size, verbosity + 2);
+        ofp_print(stdout, b.data, b.size, NULL, verbosity + 2);
     }
     ofpbuf_uninit(&b);
 
@@ -2476,7 +2580,8 @@ ofctl_ofp_parse_pcap(struct ovs_cmdl_context *ctx)
                     printf(IP_FMT".%"PRIu16" > "IP_FMT".%"PRIu16":\n",
                            IP_ARGS(flow.nw_src), ntohs(flow.tp_src),
                            IP_ARGS(flow.nw_dst), ntohs(flow.tp_dst));
-                    ofp_print(stdout, dp_packet_data(payload), length, verbosity + 1);
+                    ofp_print(stdout, dp_packet_data(payload), length,
+                              NULL, verbosity + 1);
                     dp_packet_pull(payload, length);
                 }
             }
@@ -2520,9 +2625,9 @@ ofctl_ping(struct ovs_cmdl_context *ctx)
             || reply->size != payload
             || memcmp(request->msg, reply->msg, payload)) {
             printf("Reply does not match request.  Request:\n");
-            ofp_print(stdout, request, request->size, verbosity + 2);
+            ofp_print(stdout, request, request->size, NULL, verbosity + 2);
             printf("Reply:\n");
-            ofp_print(stdout, reply, reply->size, verbosity + 2);
+            ofp_print(stdout, reply, reply->size, NULL, verbosity + 2);
         }
         printf("%"PRIu32" bytes from %s: xid=%08"PRIx32" time=%.1f ms\n",
                reply->size, ctx->argv[1], ntohl(rpy_hdr->xid),
@@ -2684,8 +2789,8 @@ ofctl_group_mod_file(int argc OVS_UNUSED, char *argv[], int command)
          * this is backwards compatible. */
         command = -2;
     }
-    error = parse_ofp_group_mod_file(argv[2], command, &gms, &n_gms,
-                                     &usable_protocols);
+    error = parse_ofp_group_mod_file(argv[2], ports_to_accept(argv[1]), command,
+                                     &gms, &n_gms, &usable_protocols);
     if (error) {
         ovs_fatal(0, "%s", error);
     }
@@ -2704,6 +2809,7 @@ ofctl_group_mod(int argc, char *argv[], uint16_t command)
         char *error;
 
         error = parse_ofp_group_mod_str(&gm, command, argc > 2 ? argv[2] : "",
+                                        ports_to_accept(argv[1]),
                                         &usable_protocols);
         if (error) {
             ovs_fatal(0, "%s", error);
@@ -2763,6 +2869,7 @@ ofctl_dump_group_stats(struct ovs_cmdl_context *ctx)
 
     error = parse_ofp_group_mod_str(&gm, OFPGC11_DELETE,
                                     ctx->argc > 2 ? ctx->argv[2] : "",
+                                    ports_to_accept(ctx->argv[1]),
                                     &usable_protocols);
     if (error) {
         ovs_fatal(0, "%s", error);
@@ -2827,8 +2934,8 @@ ofctl_bundle(struct ovs_cmdl_context *ctx)
     size_t n_bms;
     char *error;
 
-    error = parse_ofp_bundle_file(ctx->argv[2], &bms, &n_bms,
-                                  &usable_protocols);
+    error = parse_ofp_bundle_file(ctx->argv[2], ports_to_accept(ctx->argv[1]),
+                                  &bms, &n_bms, &usable_protocols);
     if (error) {
         ovs_fatal(0, "%s", error);
     }
@@ -2999,6 +3106,10 @@ struct fte_state {
 
     /* The final metadata table that we have constructed. */
     struct tun_table *tun_tab;
+
+    /* Port map.  There is only one port map, not one per source, because it
+     * only makes sense to display a single name for a given port number. */
+    const struct ofputil_port_map *port_map;
 };
 
 /* Frees 'version' and the data that it owns. */
@@ -3043,7 +3154,7 @@ fte_version_format(const struct fte_state *fte_state, const struct fte *fte,
     if (version->table_id) {
         ds_put_format(s, "table=%"PRIu8" ", version->table_id);
     }
-    cls_rule_format(&fte->rule, fte_state->tun_tab, s);
+    cls_rule_format(&fte->rule, fte_state->tun_tab, fte_state->port_map, s);
     if (version->cookie != htonll(0)) {
         ds_put_format(s, " cookie=0x%"PRIx64, ntohll(version->cookie));
     }
@@ -3058,7 +3169,8 @@ fte_version_format(const struct fte_state *fte_state, const struct fte *fte,
     }
 
     ds_put_cstr(s, " actions=");
-    ofpacts_format(version->ofpacts, version->ofpacts_len, s);
+    ofpacts_format(version->ofpacts, version->ofpacts_len,
+                   fte_state->port_map, s);
 
     ds_put_char(s, '\n');
 }
@@ -3227,6 +3339,7 @@ fte_state_init(struct fte_state *state)
 
     ovs_list_init(&state->fte_pending_list);
     state->tun_tab = NULL;
+    state->port_map = NULL;
 }
 
 static void
@@ -3311,7 +3424,8 @@ read_flows_from_file(const char *filename, struct fte_state *state, int index)
         char *error;
         enum ofputil_protocol usable;
 
-        error = parse_ofp_str(&fm, OFPFC_ADD, ds_cstr(&s), &usable);
+        error = parse_ofp_str(&fm, OFPFC_ADD, ds_cstr(&s), state->port_map,
+                              &usable);
         if (error) {
             ovs_fatal(0, "%s:%d: %s", filename, line_number, error);
         }
@@ -3421,7 +3535,7 @@ fte_make_flow_mod(const struct fte *fte, int index, uint16_t command,
 static void
 ofctl_replace_flows(struct ovs_cmdl_context *ctx)
 {
-    enum { FILE_IDX = 0, SWITCH_IDX = 1 };
+    enum { FILE_IDX = 0,  SWITCH_IDX = 1 };
     enum ofputil_protocol usable_protocols, protocol;
     struct fte_state fte_state;
     struct flow_tables tables;
@@ -3431,6 +3545,7 @@ ofctl_replace_flows(struct ovs_cmdl_context *ctx)
     struct fte *fte;
 
     fte_state_init(&fte_state);
+    fte_state.port_map = ports_to_accept(ctx->argv[1]);
     usable_protocols = read_flows_from_file(ctx->argv[2], &fte_state, FILE_IDX);
 
     protocol = open_vconn(ctx->argv[1], &vconn);
@@ -3496,6 +3611,10 @@ read_flows_from_source(const char *source, struct fte_state *state, int index)
         protocol = set_protocol_for_flow_dump(vconn, protocol, OFPUTIL_P_ANY);
         read_flows_from_switch(vconn, protocol, state, index);
         vconn_close(vconn);
+
+        if (!state->port_map) {
+            state->port_map = ports_to_show(source);
+        }
     }
 }
 
@@ -3676,7 +3795,7 @@ ofctl_parse_flows__(struct ofputil_flow_mod *fms, size_t n_fms,
         struct ofpbuf *msg;
 
         msg = ofputil_encode_flow_mod(fm, protocol);
-        ofp_print(stdout, msg->data, msg->size, verbosity);
+        ofp_print(stdout, msg->data, msg->size, NULL, verbosity);
         ofpbuf_delete(msg);
 
         free(CONST_CAST(struct ofpact *, fm->ofpacts));
@@ -3692,7 +3811,8 @@ ofctl_parse_flow(struct ovs_cmdl_context *ctx)
     struct ofputil_flow_mod fm;
     char *error;
 
-    error = parse_ofp_flow_mod_str(&fm, ctx->argv[1], OFPFC_ADD, &usable_protocols);
+    error = parse_ofp_flow_mod_str(&fm, ctx->argv[1], NULL,
+                                   OFPFC_ADD, &usable_protocols);
     if (error) {
         ovs_fatal(0, "%s", error);
     }
@@ -3709,8 +3829,8 @@ ofctl_parse_flows(struct ovs_cmdl_context *ctx)
     size_t n_fms = 0;
     char *error;
 
-    error = parse_ofp_flow_mod_file(ctx->argv[1], OFPFC_ADD, &fms, &n_fms,
-                                    &usable_protocols);
+    error = parse_ofp_flow_mod_file(ctx->argv[1], NULL, OFPFC_ADD,
+                                    &fms, &n_fms, &usable_protocols);
     if (error) {
         ovs_fatal(0, "%s", error);
     }
@@ -3903,7 +4023,7 @@ ofctl_parse_actions__(const char *version_s, bool instructions)
         /* Print cls_rule. */
         ds_init(&s);
         ds_put_cstr(&s, "actions=");
-        ofpacts_format(ofpacts.data, ofpacts.size, &s);
+        ofpacts_format(ofpacts.data, ofpacts.size, NULL, &s);
         puts(ds_cstr(&s));
         ds_destroy(&s);
 
@@ -4006,7 +4126,7 @@ ofctl_parse_ofp10_match(struct ovs_cmdl_context *ctx OVS_UNUSED)
 
         /* Convert to cls_rule and print. */
         ofputil_match_from_ofp10_match(match_in.data, &match);
-        match_print(&match);
+        match_print(&match, NULL);
 
         /* Convert back to ofp10_match and print differences from input. */
         ofputil_match_to_ofp10_match(&match, &match_out);
@@ -4062,7 +4182,7 @@ ofctl_parse_ofp11_match(struct ovs_cmdl_context *ctx OVS_UNUSED)
         }
 
         /* Print match. */
-        match_print(&match);
+        match_print(&match, NULL);
 
         /* Convert back to ofp11_match and print differences from input. */
         ofputil_match_to_ofp11_match(&match, &match_out);
@@ -4106,7 +4226,7 @@ ofctl_parse_pcap(struct ovs_cmdl_context *ctx)
 
             pkt_metadata_init(&packet->md, u32_to_odp(ofp_to_u16(OFPP_ANY)));
             flow_extract(packet, &flow);
-            flow_print(stdout, &flow);
+            flow_print(stdout, &flow, NULL);
             putchar('\n');
             dp_packet_delete(packet);
         }
@@ -4146,10 +4266,10 @@ ofctl_check_vlan(struct ovs_cmdl_context *ctx)
     match.wc.masks.vlans[0].tci = htons(strtoul(ctx->argv[2], NULL, 16));
 
     /* Convert to and from string. */
-    string_s = match_to_string(&match, OFP_DEFAULT_PRIORITY);
+    string_s = match_to_string(&match, NULL, OFP_DEFAULT_PRIORITY);
     printf("%s -> ", string_s);
     fflush(stdout);
-    error_s = parse_ofp_str(&fm, -1, string_s, &usable_protocols);
+    error_s = parse_ofp_str(&fm, -1, string_s, NULL, &usable_protocols);
     if (error_s) {
         ovs_fatal(0, "%s", error_s);
     }
@@ -4318,7 +4438,7 @@ ofctl_ofp_print(struct ovs_cmdl_context *ctx)
     if (ofpbuf_put_hex(&packet, buffer, NULL)[0] != '\0') {
         ovs_fatal(0, "trailing garbage following hex bytes");
     }
-    ofp_print(stdout, packet.data, packet.size, verbosity);
+    ofp_print(stdout, packet.data, packet.size, NULL, verbosity);
     ofpbuf_uninit(&packet);
     ds_destroy(&line);
 }
@@ -4333,7 +4453,7 @@ ofctl_encode_hello(struct ovs_cmdl_context *ctx)
 
     hello = ofputil_encode_hello(bitmap);
     ovs_hex_dump(stdout, hello->data, hello->size, 0, false);
-    ofp_print(stdout, hello->data, hello->size, verbosity);
+    ofp_print(stdout, hello->data, hello->size, NULL, verbosity);
     ofpbuf_delete(hello);
 }
 
diff --git a/utilities/ovs-testcontroller.c b/utilities/ovs-testcontroller.c
index 004632e9290c..b4a451286455 100644
--- a/utilities/ovs-testcontroller.c
+++ b/utilities/ovs-testcontroller.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2015 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2015, 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.
@@ -335,8 +335,8 @@ parse_options(int argc, char *argv[])
             break;
 
         case OPT_WITH_FLOWS:
-            error = parse_ofp_flow_mod_file(optarg, OFPFC_ADD, &default_flows,
-                                            &n_default_flows,
+            error = parse_ofp_flow_mod_file(optarg, NULL, OFPFC_ADD,
+                                            &default_flows, &n_default_flows,
                                             &usable_protocols);
             if (error) {
                 ovs_fatal(0, "%s", error);
-- 
2.10.2




More information about the dev mailing list