Thanks for the review!  I applied this series to master.

On Thu, Feb 01, 2018 at 09:57:24AM -0800, Yifeng Sun wrote:
> Thanks for the nice feature. It looks good to me.
> 
> Reviewed-by: Yifeng Sun <pkusunyif...@gmail.com>
> 
> 
> On Wed, Jan 31, 2018 at 11:36 AM, Ben Pfaff <b...@ovn.org> wrote:
> 
> > OpenFlow has little-known support for naming tables.  Open vSwitch has
> > supported table names for ages, but it has never used or displayed them
> > outside of commands dedicated to table manipulation.  This commit adds
> > support for table names in ovs-ofctl.  When a table has a name, it displays
> > that name in flows and actions, so that, for example, the following:
> >     table=1, arp, actions=resubmit(,2)
> > might become:
> >     table=ingress_acl, arp, actions=resubmit(,mac_learning)
> > given appropriately named tables.
> >
> > For backward compatibility, only interactive ovs-ofctl commands by default
> > display table names; to display them in scripts, use the new --names
> > option.
> >
> > This feature was inspired by a talk that Kei Nohguchi presented at Open
> > vSwitch 2017 Fall Conference.
> >
> > CC: Kei Nohguchi <k...@nohguchi.com>
> > Signed-off-by: Ben Pfaff <b...@ovn.org>
> > ---
> >  NEWS                              |   8 ++
> >  include/openvswitch/ofp-actions.h |   2 +
> >  include/openvswitch/ofp-parse.h   |  20 ++-
> >  include/openvswitch/ofp-print.h   |  12 +-
> >  include/openvswitch/ofp-util.h    |   8 ++
> >  lib/learn.c                       |  20 ++-
> >  lib/learn.h                       |   6 +-
> >  lib/learning-switch.c             |   2 +-
> >  lib/ofp-actions.c                 |  45 +++---
> >  lib/ofp-parse.c                   |  80 +++++++----
> >  lib/ofp-print.c                   | 202 ++++++++++++++++----------
> >  lib/ofp-util.c                    | 109 ++++++++++++--
> >  lib/vconn.c                       |  10 +-
> >  ofproto/ofproto-dpif.c            |   4 +-
> >  ofproto/ofproto.c                 |   2 +-
> >  ovn/controller/ofctrl.c           |  12 +-
> >  ovn/controller/pinctrl.c          |   2 +-
> >  ovn/utilities/ovn-sbctl.c         |   2 +-
> >  ovn/utilities/ovn-trace.c         |   2 +-
> >  tests/ofproto.at                  |  89 ++++++++----
> >  utilities/ovs-ofctl.8.in          |  90 +++++++-----
> >  utilities/ovs-ofctl.c             | 294 ++++++++++++++++++++++++++++++
> > +-------
> >  utilities/ovs-testcontroller.c    |   2 +-
> >  23 files changed, 736 insertions(+), 287 deletions(-)
> >
> > diff --git a/NEWS b/NEWS
> > index 726589ce3896..d76958b8adc0 100644
> > --- a/NEWS
> > +++ b/NEWS
> > @@ -2,6 +2,14 @@ Post-v2.9.0
> >  --------------------
> >      - ovs-vswitchd:
> >        * New options --l7 and --l7-len to "ofproto/trace" command.
> > +   - ovs-ofctl:
> > +     * ovs-ofctl now accepts and display table 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.
> > +   - ovs-vswitchd:
> > +     * Previous versions gave OpenFlow tables default names of the form
> > +       "table#".  These are not helpful names for the purpose of accepting
> > +       and displaying table names, so now tables by default have no names.
> >
> >
> >  v2.9.0 - xx xxx xxxx
> > diff --git a/include/openvswitch/ofp-actions.h b/include/openvswitch/ofp-
> > actions.h
> > index 454c705ccf73..cba027b1d945 100644
> > --- a/include/openvswitch/ofp-actions.h
> > +++ b/include/openvswitch/ofp-actions.h
> > @@ -1068,6 +1068,7 @@ uint32_t ofpacts_get_meter(const struct ofpact[],
> > size_t ofpacts_len);
> >  struct ofpact_format_params {
> >      /* Input. */
> >      const struct ofputil_port_map *port_map;
> > +    const struct ofputil_table_map *table_map;
> >
> >      /* Output. */
> >      struct ds *s;
> > @@ -1080,6 +1081,7 @@ const char *ofpact_name(enum ofpact_type);
> >  struct ofpact_parse_params {
> >      /* Input. */
> >      const struct ofputil_port_map *port_map;
> > +    const struct ofputil_table_map *table_map;
> >
> >      /* Output. */
> >      struct ofpbuf *ofpacts;
> > diff --git a/include/openvswitch/ofp-parse.h b/include/openvswitch/ofp-
> > parse.h
> > index 013a8f3edf70..c4228a50358f 100644
> > --- a/include/openvswitch/ofp-parse.h
> > +++ b/include/openvswitch/ofp-parse.h
> > @@ -45,26 +45,33 @@ enum ofputil_protocol;
> >
> >  char *parse_ofp_str(struct ofputil_flow_mod *, int command, const char
> > *str_,
> >                      const struct ofputil_port_map *,
> > +                    const struct ofputil_table_map *,
> >                      enum ofputil_protocol *usable_protocols)
> >      OVS_WARN_UNUSED_RESULT;
> >
> >  char *parse_ofp_flow_mod_str(struct ofputil_flow_mod *, const char
> > *string,
> > -                             const struct ofputil_port_map *, int command,
> > +                             const struct ofputil_port_map *,
> > +                             const struct ofputil_table_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 *,
> > +                               const struct ofputil_table_map *,
> >                                 enum ofputil_protocol *usable_protocols)
> >      OVS_WARN_UNUSED_RESULT;
> >
> >  char *parse_ofp_table_mod(struct ofputil_table_mod *,
> >                            const char *table_id, const char
> > *flow_miss_handling,
> > +                          const struct ofputil_table_map *,
> >                            uint32_t *usable_versions)
> >      OVS_WARN_UNUSED_RESULT;
> >
> >  char *parse_ofp_flow_mod_file(const char *file_name,
> > -                              const struct ofputil_port_map *, int
> > command,
> > +                              const struct ofputil_port_map *,
> > +                              const struct ofputil_table_map *,
> > +                              int command,
> >                                struct ofputil_flow_mod **fms, size_t
> > *n_fms,
> >                                enum ofputil_protocol *usable_protocols)
> >      OVS_WARN_UNUSED_RESULT;
> > @@ -72,6 +79,7 @@ char *parse_ofp_flow_mod_file(const char *file_name,
> >  char *parse_ofp_flow_stats_request_str(struct ofputil_flow_stats_request
> > *,
> >                                         bool aggregate, const char *string,
> >                                         const struct ofputil_port_map *,
> > +                                       const struct ofputil_table_map *,
> >                                         enum ofputil_protocol
> > *usable_protocols)
> >      OVS_WARN_UNUSED_RESULT;
> >
> > @@ -86,12 +94,14 @@ 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,
> > +                                 const struct ofputil_port_map *,
> > +                                 const struct ofputil_table_map *,
> >                                   enum ofputil_protocol *usable_protocols)
> >      OVS_WARN_UNUSED_RESULT;
> >
> >  char *parse_ofp_group_mod_file(const char *file_name,
> > -                               const struct ofputil_port_map *, int
> > command,
> > +                               const struct ofputil_port_map *,
> > +                               const struct ofputil_table_map *, int
> > command,
> >                                 struct ofputil_group_mod **gms, size_t
> > *n_gms,
> >                                 enum ofputil_protocol *usable_protocols)
> >      OVS_WARN_UNUSED_RESULT;
> > @@ -99,11 +109,13 @@ char *parse_ofp_group_mod_file(const char *file_name,
> >  char *parse_ofp_group_mod_str(struct ofputil_group_mod *, int command,
> >                                const char *string,
> >                                const struct ofputil_port_map *,
> > +                              const struct ofputil_table_map *,
> >                                enum ofputil_protocol *usable_protocols)
> >      OVS_WARN_UNUSED_RESULT;
> >
> >  char *parse_ofp_bundle_file(const char *file_name,
> >                              const struct ofputil_port_map *,
> > +                            const struct ofputil_table_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 d02634e3e91c..ed113786a28c 100644
> > --- a/include/openvswitch/ofp-print.h
> > +++ b/include/openvswitch/ofp-print.h
> > @@ -31,6 +31,7 @@ struct ofp_header;
> >  struct ofputil_flow_stats;
> >  struct ofputil_port_map;
> >  struct ofputil_table_features;
> > +struct ofputil_table_map;
> >  struct ofputil_table_stats;
> >  struct dp_packet;
> >
> > @@ -39,7 +40,7 @@ extern "C" {
> >  #endif
> >
> >  void ofp_print(FILE *, const void *, size_t, const struct
> > ofputil_port_map *,
> > -               int verbosity);
> > +               const struct ofputil_table_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);
> > @@ -48,7 +49,7 @@ 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, const struct ofputil_port_map *,
> > -                    int verbosity);
> > +                    const struct ofputil_table_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);
> > @@ -59,10 +60,13 @@ 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);
> > +    const struct ofputil_table_stats *prev_stats,
> > +    const struct ofputil_table_map *table_map);
> >
> >  void ofp_print_flow_stats(struct ds *, const struct ofputil_flow_stats *,
> > -                          const struct ofputil_port_map *, bool
> > show_stats);
> > +                          const struct ofputil_port_map *,
> > +                          const struct ofputil_table_map *,
> > +                          bool show_stats);
> >
> >  #ifdef  __cplusplus
> >  }
> > diff --git a/include/openvswitch/ofp-util.h b/include/openvswitch/ofp-
> > util.h
> > index d9780dd44582..5dd1b34c216b 100644
> > --- a/include/openvswitch/ofp-util.h
> > +++ b/include/openvswitch/ofp-util.h
> > @@ -816,6 +816,14 @@ void ofputil_table_map_put(struct ofputil_table_map *,
> >                             uint8_t, const char *name);
> >  void ofputil_table_map_destroy(struct ofputil_table_map *);
> >
> > +/* Table numbers. */
> > +bool ofputil_table_from_string(const char *, const struct
> > ofputil_table_map *,
> > +                               uint8_t *tablep);
> > +void ofputil_format_table(uint8_t table, const struct ofputil_table_map *,
> > +                         struct ds *);
> > +void ofputil_table_to_string(uint8_t, const struct ofputil_table_map *,
> > +                            char *namebuf, size_t bufsize);
> > +
> >  /* Abstract ofp_table_mod. */
> >  struct ofputil_table_mod {
> >      uint8_t table_id;         /* ID of the table, 0xff indicates all
> > tables. */
> > diff --git a/lib/learn.c b/lib/learn.c
> > index 9e321371c0a5..5164082f8e52 100644
> > --- a/lib/learn.c
> > +++ b/lib/learn.c
> > @@ -381,6 +381,7 @@ learn_parse_spec(const char *orig, char *name, char
> > *value,
> >   * error.  The caller is responsible for freeing the returned string. */
> >  static char * OVS_WARN_UNUSED_RESULT
> >  learn_parse__(char *orig, char *arg, const struct ofputil_port_map
> > *port_map,
> > +              const struct ofputil_table_map *table_map,
> >                struct ofpbuf *ofpacts)
> >  {
> >      struct ofpact_learn *learn;
> > @@ -396,8 +397,10 @@ learn_parse__(char *orig, char *arg, const struct
> > ofputil_port_map *port_map,
> >      match_init_catchall(&match);
> >      while (ofputil_parse_key_value(&arg, &name, &value)) {
> >          if (!strcmp(name, "table")) {
> > -            learn->table_id = atoi(value);
> > -            if (learn->table_id == 255) {
> > +            if (!ofputil_table_from_string(value, table_map,
> > +                                           &learn->table_id)) {
> > +                return xasprintf("unknown table \"%s\"", value);
> > +            } else if (learn->table_id == 255) {
> >                  return xasprintf("%s: table id 255 not valid for `learn' "
> >                                   "action", orig);
> >              }
> > @@ -465,10 +468,11 @@ learn_parse__(char *orig, char *arg, const struct
> > ofputil_port_map *port_map,
> >   * Modifies 'arg'. */
> >  char * OVS_WARN_UNUSED_RESULT
> >  learn_parse(char *arg, const struct ofputil_port_map *port_map,
> > +            const struct ofputil_table_map *table_map,
> >              struct ofpbuf *ofpacts)
> >  {
> >      char *orig = xstrdup(arg);
> > -    char *error = learn_parse__(orig, arg, port_map, ofpacts);
> > +    char *error = learn_parse__(orig, arg, port_map, table_map, ofpacts);
> >      free(orig);
> >      return error;
> >  }
> > @@ -477,16 +481,18 @@ learn_parse(char *arg, const struct ofputil_port_map
> > *port_map,
> >   * describes. */
> >  void
> >  learn_format(const struct ofpact_learn *learn,
> > -             const struct ofputil_port_map *port_map, struct ds *s)
> > +             const struct ofputil_port_map *port_map,
> > +             const struct ofputil_table_map *table_map,
> > +             struct ds *s)
> >  {
> >      const struct ofpact_learn_spec *spec;
> >      struct match match;
> >
> >      match_init_catchall(&match);
> >
> > -    ds_put_format(s, "%slearn(%s%stable=%s%"PRIu8,
> > -                  colors.learn, colors.end, colors.special, colors.end,
> > -                  learn->table_id);
> > +    ds_put_format(s, "%slearn(%s%stable=%s",
> > +                  colors.learn, colors.end, colors.special, colors.end);
> > +    ofputil_format_table(learn->table_id, table_map, s);
> >      if (learn->idle_timeout != OFP_FLOW_PERMANENT) {
> >          ds_put_format(s, ",%sidle_timeout=%s%"PRIu16,
> >                        colors.param, colors.end, learn->idle_timeout);
> > diff --git a/lib/learn.h b/lib/learn.h
> > index 31d3a14e827a..2bdfee702dac 100644
> > --- a/lib/learn.h
> > +++ b/lib/learn.h
> > @@ -28,6 +28,7 @@ struct ofpbuf;
> >  struct ofpact_learn;
> >  struct ofputil_flow_mod;
> >  struct ofputil_port_map;
> > +struct ofputil_table_map;
> >  struct nx_action_learn;
> >
> >  /* NXAST_LEARN helper functions.
> > @@ -41,9 +42,10 @@ void learn_execute(const struct ofpact_learn *, const
> > struct flow *,
> >  void learn_mask(const struct ofpact_learn *, struct flow_wildcards *);
> >
> >  char *learn_parse(char *, const struct ofputil_port_map *,
> > -                  struct ofpbuf *ofpacts)
> > +                  const struct ofputil_table_map *, struct ofpbuf
> > *ofpacts)
> >      OVS_WARN_UNUSED_RESULT;
> >  void learn_format(const struct ofpact_learn *,
> > -                  const struct ofputil_port_map *, struct ds *);
> > +                  const struct ofputil_port_map *,
> > +                  const struct ofputil_table_map *, struct ds *);
> >
> >  #endif /* learn.h */
> > diff --git a/lib/learning-switch.c b/lib/learning-switch.c
> > index 5b014e5f3757..f840f034875f 100644
> > --- a/lib/learning-switch.c
> > +++ b/lib/learning-switch.c
> > @@ -369,7 +369,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, NULL, 2);
> > +        char *s = ofp_to_string(msg->data, msg->size, NULL, NULL, 2);
> >          VLOG_DBG_RL(&rl, "%016llx: OpenFlow packet ignored: %s",
> >                      sw->datapath_id, s);
> >          free(s);
> > diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
> > index 1000d6d1de6d..80dbd6dc57d6 100644
> > --- a/lib/ofp-actions.c
> > +++ b/lib/ofp-actions.c
> > @@ -4398,14 +4398,10 @@ parse_RESUBMIT(char *arg, const struct
> > ofpact_parse_params *pp)
> >
> >      table_s = strsep(&arg, ",");
> >      if (table_s && table_s[0]) {
> > -        uint32_t table_id = 0;
> > -        char *error;
> > -
> > -        error = str_to_u32(table_s, &table_id);
> > -        if (error) {
> > -            return error;
> > +        if (!ofputil_table_from_string(table_s, pp->table_map,
> > +                                       &resubmit->table_id)) {
> > +            return xasprintf("%s: resubmit to unknown table", table_s);
> >          }
> > -        resubmit->table_id = table_id;
> >      } else {
> >          resubmit->table_id = 255;
> >      }
> > @@ -4441,7 +4437,7 @@ format_RESUBMIT(const struct ofpact_resubmit *a,
> >          }
> >          ds_put_char(fp->s, ',');
> >          if (a->table_id != 255) {
> > -            ds_put_format(fp->s, "%"PRIu8, a->table_id);
> > +            ofputil_format_table(a->table_id, fp->table_map, fp->s);
> >          }
> >          if (a->with_ct_orig) {
> >              ds_put_cstr(fp->s, ",ct");
> > @@ -5024,14 +5020,14 @@ encode_LEARN(const struct ofpact_learn *learn,
> >  static char * OVS_WARN_UNUSED_RESULT
> >  parse_LEARN(char *arg, const struct ofpact_parse_params *pp)
> >  {
> > -    return learn_parse(arg, pp->port_map, pp->ofpacts);
> > +    return learn_parse(arg, pp->port_map, pp->table_map, pp->ofpacts);
> >  }
> >
> >  static void
> >  format_LEARN(const struct ofpact_learn *a,
> >               const struct ofpact_format_params *fp)
> >  {
> > -    learn_format(a, fp->port_map, fp->s);
> > +    learn_format(a, fp->port_map, fp->table_map, fp->s);
> >  }
> >
> >  /* Action structure for NXAST_CONJUNCTION. */
> > @@ -5372,10 +5368,11 @@ static void
> >  format_UNROLL_XLATE(const struct ofpact_unroll_xlate *a,
> >                      const struct ofpact_format_params *fp)
> >  {
> > -    ds_put_format(fp->s, "%sunroll_xlate(%s%stable=%s%"PRIu8
> > -                  ", %scookie=%s%"PRIu64"%s)%s",
> > +    ds_put_format(fp->s, "%sunroll_xlate(%s%stable=%s",
> >                    colors.paren,   colors.end,
> > -                  colors.special, colors.end, a->rule_table_id,
> > +                  colors.special, colors.end);
> > +    ofputil_format_table(a->rule_table_id, fp->table_map, fp->s);
> > +    ds_put_format(fp->s, ", %scookie=%s%"PRIu64"%s)%s",
> >                    colors.param,   colors.end, ntohll(a->rule_cookie),
> >                    colors.paren,   colors.end);
> >  }
> > @@ -6038,8 +6035,10 @@ parse_CT(char *arg, const struct
> > ofpact_parse_params *pp)
> >          } else if (!strcmp(key, "force")) {
> >              oc->flags |= NX_CT_F_FORCE;
> >          } else if (!strcmp(key, "table")) {
> > -            error = str_to_u8(value, "recirc_table", &oc->recirc_table);
> > -            if (!error && oc->recirc_table == NX_CT_RECIRC_NONE) {
> > +            if (!ofputil_table_from_string(value, pp->table_map,
> > +                                           &oc->recirc_table)) {
> > +                error = xasprintf("unknown table %s", value);
> > +            } else if (oc->recirc_table == NX_CT_RECIRC_NONE) {
> >                  error = xasprintf("invalid table %#"PRIx8,
> > oc->recirc_table);
> >              }
> >          } else if (!strcmp(key, "zone")) {
> > @@ -6125,8 +6124,9 @@ format_CT(const struct ofpact_conntrack *a,
> >          ds_put_format(fp->s, "%sforce%s,", colors.value, colors.end);
> >      }
> >      if (a->recirc_table != NX_CT_RECIRC_NONE) {
> > -        ds_put_format(fp->s, "%stable=%s%"PRIu8",",
> > -                      colors.special, colors.end, a->recirc_table);
> > +        ds_put_format(fp->s, "%stable=%s", colors.special, colors.end);
> > +        ofputil_format_table(a->recirc_table, fp->table_map, fp->s);
> > +        ds_put_char(fp->s, ',');
> >      }
> >      if (a->zone_src.field) {
> >          ds_put_format(fp->s, "%szone=%s", colors.param, colors.end);
> > @@ -6816,19 +6816,18 @@ static char * OVS_WARN_UNUSED_RESULT
> >  parse_GOTO_TABLE(char *arg, const struct ofpact_parse_params *pp)
> >  {
> >      struct ofpact_goto_table *ogt = ofpact_put_GOTO_TABLE(pp->ofpacts);
> > -    char *table_s = strsep(&arg, ",");
> > -    if (!table_s || !table_s[0]) {
> > -        return xstrdup("instruction goto-table needs table id");
> > +    if (!ofputil_table_from_string(arg, pp->table_map, &ogt->table_id)) {
> > +        return xasprintf("unknown table \"%s\"", arg);
> >      }
> > -    return str_to_u8(table_s, "table", &ogt->table_id);
> > +    return NULL;
> >  }
> >
> >  static void
> >  format_GOTO_TABLE(const struct ofpact_goto_table *a,
> >                    const struct ofpact_format_params *fp)
> >  {
> > -    ds_put_format(fp->s, "%sgoto_table:%s%"PRIu8,
> > -                  colors.param, colors.end, a->table_id);
> > +    ds_put_format(fp->s, "%sgoto_table:%s", colors.param, colors.end);
> > +    ofputil_format_table(a->table_id, fp->table_map, fp->s);
> >  }
> >
> >  static void
> > diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c
> > index b68081740f09..1e30c20f4966 100644
> > --- a/lib/ofp-parse.c
> > +++ b/lib/ofp-parse.c
> > @@ -322,6 +322,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,
> > +                const struct ofputil_table_map *table_map,
> >                  enum ofputil_protocol *usable_protocols)
> >  {
> >      enum {
> > @@ -450,7 +451,10 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int
> > command, char *string,
> >              }
> >
> >              if (!strcmp(name, "table")) {
> > -                error = str_to_u8(value, "table", &fm->table_id);
> > +                if (!ofputil_table_from_string(value, table_map,
> > +                                               &fm->table_id)) {
> > +                    return xasprintf("unknown table \"%s\"", value);
> > +                }
> >                  if (fm->table_id != 0xff) {
> >                      *usable_protocols &= OFPUTIL_P_TID;
> >                  }
> > @@ -559,6 +563,7 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int
> > command, char *string,
> >          ofpbuf_init(&ofpacts, 32);
> >          struct ofpact_parse_params pp = {
> >              .port_map = port_map,
> > +            .table_map = table_map,
> >              .ofpacts = &ofpacts,
> >              .usable_protocols = &action_usable_protocols
> >          };
> > @@ -610,12 +615,14 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int
> > command, char *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,
> > +              const struct ofputil_table_map *table_map,
> >                enum ofputil_protocol *usable_protocols)
> >  {
> >      char *string = xstrdup(str_);
> >      char *error;
> >
> > -    error = parse_ofp_str__(fm, command, string, port_map,
> > usable_protocols);
> > +    error = parse_ofp_str__(fm, command, string, port_map, table_map,
> > +                            usable_protocols);
> >      if (error) {
> >          fm->ofpacts = NULL;
> >          fm->ofpacts_len = 0;
> > @@ -630,6 +637,7 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int
> > command, const char *str_,
> >  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,
> > +                           const struct ofputil_table_map *table_map,
> >                             enum ofputil_protocol *usable_protocols)
> >  {
> >      enum ofputil_protocol action_usable_protocols;
> > @@ -719,6 +727,7 @@ parse_ofp_packet_out_str__(struct ofputil_packet_out
> > *po, char *string,
> >      if (act_str) {
> >          struct ofpact_parse_params pp = {
> >              .port_map = port_map,
> > +            .table_map = table_map,
> >              .ofpacts = &ofpacts,
> >              .usable_protocols = &action_usable_protocols,
> >          };
> > @@ -750,12 +759,14 @@ out:
> >  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,
> > +                         const struct ofputil_table_map *table_map,
> >                           enum ofputil_protocol *usable_protocols)
> >  {
> >      char *string = xstrdup(str_);
> >      char *error;
> >
> > -    error = parse_ofp_packet_out_str__(po, string, port_map,
> > usable_protocols);
> > +    error = parse_ofp_packet_out_str__(po, string, port_map, table_map,
> > +                                       usable_protocols);
> >      if (error) {
> >          po->ofpacts = NULL;
> >          po->ofpacts_len = 0;
> > @@ -996,6 +1007,7 @@ static 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,
> > +                             const struct ofputil_table_map *table_map,
> >                               char *string,
> >                               enum ofputil_protocol *usable_protocols)
> >  {
> > @@ -1040,7 +1052,10 @@ parse_flow_monitor_request__(struct
> > ofputil_flow_monitor_request *fmr,
> >              }
> >
> >              if (!strcmp(name, "table")) {
> > -                error = str_to_u8(value, "table", &fmr->table_id);
> > +                if (!ofputil_table_from_string(value, table_map,
> > +                                               &fmr->table_id)) {
> > +                    error = xasprintf("unknown table \"%s\"", value);
> > +                }
> >              } else if (!strcmp(name, "out_port")) {
> >                  fmr->out_port = u16_to_ofp(atoi(value));
> >              } else {
> > @@ -1064,11 +1079,12 @@ 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,
> > +                           const struct ofputil_table_map *table_map,
> >                             enum ofputil_protocol *usable_protocols)
> >  {
> >      char *string = xstrdup(str_);
> > -    char *error = parse_flow_monitor_request__(fmr, str_, port_map,
> > string,
> > -                                               usable_protocols);
> > +    char *error = parse_flow_monitor_request__(fmr, str_, port_map,
> > table_map,
> > +                                               string, usable_protocols);
> >      free(string);
> >      return error;
> >  }
> > @@ -1084,10 +1100,12 @@ 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,
> > -                       const struct ofputil_port_map *port_map, int
> > command,
> > +                       const struct ofputil_port_map *port_map,
> > +                       const struct ofputil_table_map *table_map,
> > +                       int command,
> >                         enum ofputil_protocol *usable_protocols)
> >  {
> > -    char *error = parse_ofp_str(fm, command, string, port_map,
> > +    char *error = parse_ofp_str(fm, command, string, port_map, table_map,
> >                                  usable_protocols);
> >
> >      if (!error) {
> > @@ -1167,16 +1185,16 @@ exit:
> >   * error.  The caller is responsible for freeing the returned string. */
> >  char * OVS_WARN_UNUSED_RESULT
> >  parse_ofp_table_mod(struct ofputil_table_mod *tm, const char *table_id,
> > -                    const char *setting, uint32_t *usable_versions)
> > +                    const char *setting,
> > +                    const struct ofputil_table_map *table_map,
> > +                    uint32_t *usable_versions)
> >  {
> >      *usable_versions = 0;
> >      if (!strcasecmp(table_id, "all")) {
> >          tm->table_id = OFPTT_ALL;
> > -    } else {
> > -        char *error = str_to_u8(table_id, "table_id", &tm->table_id);
> > -        if (error) {
> > -            return error;
> > -        }
> > +    } else if (!ofputil_table_from_string(table_id, table_map,
> > +                                          &tm->table_id)) {
> > +        return xasprintf("unknown table \"%s\"", table_id);
> >      }
> >
> >      tm->miss = OFPUTIL_TABLE_MISS_DEFAULT;
> > @@ -1240,7 +1258,9 @@ parse_ofp_table_mod(struct ofputil_table_mod *tm,
> > const char *table_id,
> >   * error.  The caller is responsible for freeing the returned string. */
> >  char * OVS_WARN_UNUSED_RESULT
> >  parse_ofp_flow_mod_file(const char *file_name,
> > -                        const struct ofputil_port_map *port_map, int
> > command,
> > +                        const struct ofputil_port_map *port_map,
> > +                        const struct ofputil_table_map *table_map,
> > +                        int command,
> >                          struct ofputil_flow_mod **fms, size_t *n_fms,
> >                          enum ofputil_protocol *usable_protocols)
> >  {
> > @@ -1271,7 +1291,7 @@ parse_ofp_flow_mod_file(const char *file_name,
> >              *fms = x2nrealloc(*fms, &allocated_fms, sizeof **fms);
> >          }
> >          error = parse_ofp_flow_mod_str(&(*fms)[*n_fms], ds_cstr(&s),
> > port_map,
> > -                                       command, &usable);
> > +                                       table_map, command, &usable);
> >          if (error) {
> >              char *err_msg;
> >              size_t i;
> > @@ -1307,12 +1327,14 @@ 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,
> > +                                 const struct ofputil_table_map
> > *table_map,
> >                                   enum ofputil_protocol *usable_protocols)
> >  {
> >      struct ofputil_flow_mod fm;
> >      char *error;
> >
> > -    error = parse_ofp_str(&fm, -1, string, port_map, usable_protocols);
> > +    error = parse_ofp_str(&fm, -1, string, port_map, table_map,
> > +                          usable_protocols);
> >      if (error) {
> >          return error;
> >      }
> > @@ -1438,8 +1460,9 @@ exit:
> >
> >  static char * OVS_WARN_UNUSED_RESULT
> >  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)
> > +                 const struct ofputil_port_map *port_map,
> > +                 const struct ofputil_table_map *table_map,
> > +                 uint8_t group_type, enum ofputil_protocol
> > *usable_protocols)
> >  {
> >      char *pos, *key, *value;
> >      struct ofpbuf ofpacts;
> > @@ -1497,6 +1520,7 @@ parse_bucket_str(struct ofputil_bucket *bucket, char
> > *str_,
> >      ofpbuf_init(&ofpacts, 0);
> >      struct ofpact_parse_params pp = {
> >          .port_map = port_map,
> > +        .table_map = table_map,
> >          .ofpacts = &ofpacts,
> >          .usable_protocols = usable_protocols,
> >      };
> > @@ -1575,6 +1599,7 @@ 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,
> > +                          const struct ofputil_table_map *table_map,
> >                            enum ofputil_protocol *usable_protocols)
> >  {
> >      enum {
> > @@ -1828,7 +1853,7 @@ 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, port_map,
> > +        error = parse_bucket_str(bucket, bkt_str, port_map, table_map,
> >                                   gm->type, usable_protocols);
> >          if (error) {
> >              free(bucket);
> > @@ -1862,11 +1887,12 @@ 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,
> > +                        const struct ofputil_table_map *table_map,
> >                          enum ofputil_protocol *usable_protocols)
> >  {
> >      char *string = xstrdup(str_);
> > -    char *error = parse_ofp_group_mod_str__(gm, command, string,
> > -                                            port_map, usable_protocols);
> > +    char *error = parse_ofp_group_mod_str__(gm, command, string, port_map,
> > +                                            table_map, usable_protocols);
> >      free(string);
> >      return error;
> >  }
> > @@ -1878,6 +1904,7 @@ parse_ofp_group_mod_str(struct ofputil_group_mod
> > *gm, int command,
> >  char * OVS_WARN_UNUSED_RESULT
> >  parse_ofp_group_mod_file(const char *file_name,
> >                           const struct ofputil_port_map *port_map,
> > +                         const struct ofputil_table_map *table_map,
> >                           int command,
> >                           struct ofputil_group_mod **gms, size_t *n_gms,
> >                           enum ofputil_protocol *usable_protocols)
> > @@ -1915,7 +1942,7 @@ parse_ofp_group_mod_file(const char *file_name,
> >              *gms = new_gms;
> >          }
> >          error = parse_ofp_group_mod_str(&(*gms)[*n_gms], command,
> > ds_cstr(&s),
> > -                                        port_map, &usable);
> > +                                        port_map, table_map, &usable);
> >          if (error) {
> >              size_t i;
> >
> > @@ -1956,6 +1983,7 @@ parse_ofp_group_mod_file(const char *file_name,
> >  char * OVS_WARN_UNUSED_RESULT
> >  parse_ofp_bundle_file(const char *file_name,
> >                        const struct ofputil_port_map *port_map,
> > +                      const struct ofputil_table_map *table_map,
> >                        struct ofputil_bundle_msg **bms, size_t *n_bms,
> >                        enum ofputil_protocol *usable_protocols)
> >  {
> > @@ -2003,7 +2031,7 @@ 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,
> > port_map,
> > -                                           -2, &usable);
> > +                                           table_map, -2, &usable);
> >              if (error) {
> >                  break;
> >              }
> > @@ -2011,7 +2039,7 @@ 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,
> > -                                            port_map, &usable);
> > +                                            port_map, table_map, &usable);
> >              if (error) {
> >                  break;
> >              }
> > @@ -2019,7 +2047,7 @@ parse_ofp_bundle_file(const char *file_name,
> >          } else if (!strncmp(s, "packet-out", len)) {
> >              s += len;
> >              error = parse_ofp_packet_out_str(&(*bms)[*n_bms].po, s,
> > port_map,
> > -                                             &usable);
> > +                                             table_map, &usable);
> >              if (error) {
> >                  break;
> >              }
> > diff --git a/lib/ofp-print.c b/lib/ofp-print.c
> > index 639d3bddf36d..dede3197cb98 100644
> > --- a/lib/ofp-print.c
> > +++ b/lib/ofp-print.c
> > @@ -119,7 +119,8 @@ format_hex_arg(struct ds *s, const uint8_t *data,
> > size_t len)
> >
> >  static enum ofperr
> >  ofp_print_packet_in(struct ds *string, const struct ofp_header *oh,
> > -                    const struct ofputil_port_map *port_map, int
> > verbosity)
> > +                    const struct ofputil_port_map *port_map,
> > +                    const struct ofputil_table_map *table_map, int
> > verbosity)
> >  {
> >      char reasonbuf[OFPUTIL_PACKET_IN_REASON_BUFSIZE];
> >      struct ofputil_packet_in_private pin;
> > @@ -134,8 +135,10 @@ ofp_print_packet_in(struct ds *string, const struct
> > ofp_header *oh,
> >          return error;
> >      }
> >
> > -    if (public->table_id) {
> > -        ds_put_format(string, " table_id=%"PRIu8, public->table_id);
> > +    if (public->table_id
> > +        || ofputil_table_map_get_name(table_map, public->table_id)) {
> > +        ds_put_format(string, " table_id=");
> > +        ofputil_format_table(public->table_id, table_map, string);
> >      }
> >
> >      if (public->cookie != OVS_BE64_MAX) {
> > @@ -207,6 +210,7 @@ ofp_print_packet_in(struct ds *string, const struct
> > ofp_header *oh,
> >
> >      struct ofpact_format_params fp = {
> >          .port_map = port_map,
> > +        .table_map = table_map,
> >          .s = string,
> >      };
> >
> > @@ -240,7 +244,8 @@ ofp_print_packet_in(struct ds *string, const struct
> > ofp_header *oh,
> >
> >  static enum ofperr
> >  ofp_print_packet_out(struct ds *string, const struct ofp_header *oh,
> > -                     const struct ofputil_port_map *port_map, int
> > verbosity)
> > +                     const struct ofputil_port_map *port_map,
> > +                     const struct ofputil_table_map *table_map, int
> > verbosity)
> >  {
> >      struct ofputil_packet_out po;
> >      struct ofpbuf ofpacts;
> > @@ -259,6 +264,7 @@ ofp_print_packet_out(struct ds *string, const struct
> > ofp_header *oh,
> >      ds_put_cstr(string, " actions=");
> >      struct ofpact_format_params fp = {
> >          .port_map = port_map,
> > +        .table_map = table_map,
> >          .s = string,
> >      };
> >      ofpacts_format(po.ofpacts, po.ofpacts_len, &fp);
> > @@ -815,7 +821,8 @@ ofp_print_flow_flags(struct ds *s, enum
> > ofputil_flow_mod_flags flags)
> >
> >  static enum ofperr
> >  ofp_print_flow_mod(struct ds *s, const struct ofp_header *oh,
> > -                   const struct ofputil_port_map *port_map, int verbosity)
> > +                   const struct ofputil_port_map *port_map,
> > +                   const struct ofputil_table_map *table_map, int
> > verbosity)
> >  {
> >      struct ofputil_flow_mod fm;
> >      struct ofpbuf ofpacts;
> > @@ -855,8 +862,10 @@ ofp_print_flow_mod(struct ds *s, const struct
> > ofp_header *oh,
> >      default:
> >          ds_put_format(s, "cmd:%d", fm.command);
> >      }
> > -    if (fm.table_id != 0) {
> > -        ds_put_format(s, " table:%d", fm.table_id);
> > +    if (fm.table_id != 0
> > +        || ofputil_table_map_get_name(table_map, fm.table_id)) {
> > +        ds_put_format(s, " table:");
> > +        ofputil_format_table(fm.table_id, table_map, s);
> >      }
> >
> >      ds_put_char(s, ' ');
> > @@ -927,6 +936,7 @@ ofp_print_flow_mod(struct ds *s, const struct
> > ofp_header *oh,
> >      ds_put_cstr(s, "actions=");
> >      struct ofpact_format_params fp = {
> >          .port_map = port_map,
> > +        .table_map = table_map,
> >          .s = s,
> >      };
> >      ofpacts_format(fm.ofpacts, fm.ofpacts_len, &fp);
> > @@ -994,7 +1004,8 @@ ofp_flow_removed_reason_to_string(enum
> > ofp_flow_removed_reason reason,
> >
> >  static enum ofperr
> >  ofp_print_flow_removed(struct ds *string, const struct ofp_header *oh,
> > -                       const struct ofputil_port_map *port_map)
> > +                       const struct ofputil_port_map *port_map,
> > +                       const struct ofputil_table_map *table_map)
> >  {
> >      char reasonbuf[OFP_FLOW_REMOVED_REASON_BUFSIZE];
> >      struct ofputil_flow_removed fr;
> > @@ -1013,7 +1024,8 @@ ofp_print_flow_removed(struct ds *string, const
> > struct ofp_header *oh,
> >                                                      sizeof reasonbuf));
> >
> >      if (fr.table_id != 255) {
> > -        ds_put_format(string, " table_id=%"PRIu8, fr.table_id);
> > +        ds_put_format(string, " table_id=");
> > +        ofputil_format_table(fr.table_id, table_map, string);
> >      }
> >
> >      if (fr.cookie != htonll(0)) {
> > @@ -1133,7 +1145,8 @@ ofputil_table_vacancy_to_string(enum
> > ofputil_table_vacancy vacancy)
> >  }
> >
> >  static enum ofperr
> > -ofp_print_table_mod(struct ds *string, const struct ofp_header *oh)
> > +ofp_print_table_mod(struct ds *string, const struct ofp_header *oh,
> > +                  const struct ofputil_table_map *table_map)
> >  {
> >      struct ofputil_table_mod pm;
> >      enum ofperr error;
> > @@ -1146,7 +1159,8 @@ ofp_print_table_mod(struct ds *string, const struct
> > ofp_header *oh)
> >      if (pm.table_id == 0xff) {
> >          ds_put_cstr(string, " table_id: ALL_TABLES");
> >      } else {
> > -        ds_put_format(string, " table_id=%"PRIu8, pm.table_id);
> > +        ds_put_format(string, " table_id=");
> > +        ofputil_format_table(pm.table_id, table_map, string);
> >      }
> >
> >      if (pm.miss != OFPUTIL_TABLE_MISS_DEFAULT) {
> > @@ -1176,9 +1190,11 @@ ofp_print_table_mod(struct ds *string, const struct
> > ofp_header *oh)
> >
> >  /* This function will print the Table description properties. */
> >  static void
> > -ofp_print_table_desc(struct ds *string, const struct ofputil_table_desc
> > *td)
> > +ofp_print_table_desc(struct ds *string, const struct ofputil_table_desc
> > *td,
> > +                     const struct ofputil_table_map *table_map)
> >  {
> > -    ds_put_format(string, "\n  table %"PRIu8, td->table_id);
> > +    ds_put_format(string, "\n  table ");
> > +    ofputil_format_table(td->table_id, table_map, string);
> >      ds_put_cstr(string, ":\n");
> >      ds_put_format(string, "   eviction=%s eviction_flags=",
> >                    ofputil_table_eviction_to_string(td->eviction));
> > @@ -1198,7 +1214,8 @@ ofp_print_table_desc(struct ds *string, const struct
> > ofputil_table_desc *td)
> >  }
> >
> >  static enum ofperr
> > -ofp_print_table_status_message(struct ds *string, const struct
> > ofp_header *oh)
> > +ofp_print_table_status_message(struct ds *string, const struct
> > ofp_header *oh,
> > +                               const struct ofputil_table_map *table_map)
> >  {
> >      struct ofputil_table_status ts;
> >      enum ofperr error;
> > @@ -1215,7 +1232,7 @@ ofp_print_table_status_message(struct ds *string,
> > const struct ofp_header *oh)
> >      }
> >
> >      ds_put_format(string, "\ntable_desc:-");
> > -    ofp_print_table_desc(string, &ts.desc);
> > +    ofp_print_table_desc(string, &ts.desc, table_map);
> >
> >      return 0;
> >  }
> > @@ -1606,7 +1623,8 @@ ofp_print_hello(struct ds *string, const struct
> > ofp_header *oh)
> >
> >  static enum ofperr
> >  ofp_print_error_msg(struct ds *string, const struct ofp_header *oh,
> > -                    const struct ofputil_port_map *port_map)
> > +                    const struct ofputil_port_map *port_map,
> > +                    const struct ofputil_table_map *table_map)
> >  {
> >      struct ofpbuf payload;
> >      enum ofperr error;
> > @@ -1622,7 +1640,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, port_map, 1);
> > +        s = ofp_to_string(payload.data, payload.size, port_map,
> > table_map, 1);
> >          ds_put_cstr(string, s);
> >          free(s);
> >      }
> > @@ -1676,7 +1694,8 @@ ofp_print_ofpst_desc_reply(struct ds *string, const
> > struct ofp_header *oh)
> >
> >  static enum ofperr
> >  ofp_print_flow_stats_request(struct ds *string, const struct ofp_header
> > *oh,
> > -                             const struct ofputil_port_map *port_map)
> > +                             const struct ofputil_port_map *port_map,
> > +                             const struct ofputil_table_map *table_map)
> >  {
> >      struct ofputil_flow_stats_request fsr;
> >      enum ofperr error;
> > @@ -1687,7 +1706,8 @@ ofp_print_flow_stats_request(struct ds *string,
> > const struct ofp_header *oh,
> >      }
> >
> >      if (fsr.table_id != 0xff) {
> > -        ds_put_format(string, " table=%"PRIu8, fsr.table_id);
> > +        ds_put_format(string, " table=");
> > +        ofputil_format_table(fsr.table_id, table_map, string);
> >      }
> >
> >      if (fsr.out_port != OFPP_ANY) {
> > @@ -1707,7 +1727,9 @@ ofp_print_flow_stats_request(struct ds *string,
> > const struct ofp_header *oh,
> >   * ages, otherwise they are omitted. */
> >  void
> >  ofp_print_flow_stats(struct ds *string, const struct ofputil_flow_stats
> > *fs,
> > -                     const struct ofputil_port_map *port_map, bool
> > show_stats)
> > +                     const struct ofputil_port_map *port_map,
> > +                     const struct ofputil_table_map *table_map,
> > +                     bool show_stats)
> >  {
> >      if (show_stats || fs->cookie) {
> >          ds_put_format(string, "%scookie=%s0x%"PRIx64", ",
> > @@ -1719,9 +1741,11 @@ ofp_print_flow_stats(struct ds *string, const
> > struct ofputil_flow_stats *fs,
> >          ds_put_cstr(string, ", ");
> >      }
> >
> > -    if (show_stats || fs->table_id) {
> > -        ds_put_format(string, "%stable=%s%"PRIu8", ",
> > -                      colors.special, colors.end, fs->table_id);
> > +    if (show_stats || fs->table_id
> > +        || ofputil_table_map_get_name(table_map, fs->table_id) != NULL) {
> > +        ds_put_format(string, "%stable=%s", colors.special, colors.end);
> > +        ofputil_format_table(fs->table_id, table_map, string);
> > +        ds_put_cstr(string, ", ");
> >      }
> >      if (show_stats) {
> >          ds_put_format(string, "%sn_packets=%s%"PRIu64", ",
> > @@ -1764,6 +1788,7 @@ ofp_print_flow_stats(struct ds *string, const struct
> > ofputil_flow_stats *fs,
> >      ds_put_format(string, "%sactions=%s", colors.actions, colors.end);
> >      struct ofpact_format_params fp = {
> >          .port_map = port_map,
> > +        .table_map = table_map,
> >          .s = string,
> >      };
> >      ofpacts_format(fs->ofpacts, fs->ofpacts_len, &fp);
> > @@ -1771,7 +1796,8 @@ ofp_print_flow_stats(struct ds *string, const struct
> > ofputil_flow_stats *fs,
> >
> >  static enum ofperr
> >  ofp_print_flow_stats_reply(struct ds *string, const struct ofp_header
> > *oh,
> > -                           const struct ofputil_port_map *port_map)
> > +                           const struct ofputil_port_map *port_map,
> > +                           const struct ofputil_table_map *table_map)
> >  {
> >      struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
> >      struct ofpbuf ofpacts;
> > @@ -1786,7 +1812,7 @@ ofp_print_flow_stats_reply(struct ds *string, const
> > struct ofp_header *oh,
> >              break;
> >          }
> >          ds_put_cstr(string, "\n ");
> > -        ofp_print_flow_stats(string, &fs, port_map, true);
> > +        ofp_print_flow_stats(string, &fs, port_map, table_map, true);
> >       }
> >      ofpbuf_uninit(&ofpacts);
> >
> > @@ -1991,7 +2017,8 @@ ofp_print_ofpst_port_reply(struct ds *string, const
> > struct ofp_header *oh,
> >  }
> >
> >  static enum ofperr
> > -ofp_print_table_stats_reply(struct ds *string, const struct ofp_header
> > *oh)
> > +ofp_print_table_stats_reply(struct ds *string, const struct ofp_header
> > *oh,
> > +                            const struct ofputil_table_map *table_map)
> >  {
> >      struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
> >      ofpraw_pull_assert(&b);
> > @@ -2011,7 +2038,8 @@ ofp_print_table_stats_reply(struct ds *string,
> > const struct ofp_header *oh)
> >          ds_put_char(string, '\n');
> >          ofp_print_table_features(string,
> >                                   &features, i ? &prev_features : NULL,
> > -                                 &stats, i ? &prev_stats : NULL);
> > +                                 &stats, i ? &prev_stats : NULL,
> > +                                 table_map);
> >          prev_features = features;
> >          prev_stats = stats;
> >      }
> > @@ -2469,7 +2497,8 @@ nx_flow_monitor_flags_to_name(uint32_t bit)
> >  static enum ofperr
> >  ofp_print_nxst_flow_monitor_request(struct ds *string,
> >                                      const struct ofp_header *oh,
> > -                                    const struct ofputil_port_map
> > *port_map)
> > +                                    const struct ofputil_port_map
> > *port_map,
> > +                                    const struct ofputil_table_map
> > *table_map)
> >  {
> >      struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
> >      for (;;) {
> > @@ -2491,7 +2520,8 @@ ofp_print_nxst_flow_monitor_request(struct ds
> > *string,
> >          }
> >
> >          if (request.table_id != 0xff) {
> > -            ds_put_format(string, " table=%"PRIu8, request.table_id);
> > +            ds_put_format(string, " table=");
> > +            ofputil_format_table(request.table_id, table_map, string);
> >          }
> >
> >          ds_put_char(string, ' ');
> > @@ -2503,7 +2533,8 @@ ofp_print_nxst_flow_monitor_request(struct ds
> > *string,
> >  static enum ofperr
> >  ofp_print_nxst_flow_monitor_reply(struct ds *string,
> >                                    const struct ofp_header *oh,
> > -                                  const struct ofputil_port_map *port_map)
> > +                                  const struct ofputil_port_map *port_map,
> > +                                  const struct ofputil_table_map
> > *table_map)
> >  {
> >      uint64_t ofpacts_stub[1024 / 8];
> >      struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub);
> > @@ -2542,7 +2573,8 @@ ofp_print_nxst_flow_monitor_reply(struct ds *string,
> >              continue;
> >          }
> >
> > -        ds_put_format(string, " table=%"PRIu8, update.table_id);
> > +        ds_put_format(string, " table=");
> > +        ofputil_format_table(update.table_id, table_map, string);
> >          if (update.idle_timeout != OFP_FLOW_PERMANENT) {
> >              ds_put_format(string, " idle_timeout=%"PRIu16,
> >                            update.idle_timeout);
> > @@ -2563,6 +2595,7 @@ ofp_print_nxst_flow_monitor_reply(struct ds *string,
> >              ds_put_cstr(string, "actions=");
> >              struct ofpact_format_params fp = {
> >                  .port_map = port_map,
> > +                .table_map = table_map,
> >                  .s = string,
> >              };
> >              ofpacts_format(update.ofpacts, update.ofpacts_len, &fp);
> > @@ -2643,7 +2676,8 @@ 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,
> > -                const struct ofputil_port_map *port_map)
> > +                const struct ofputil_port_map *port_map,
> > +                const struct ofputil_table_map *table_map)
> >  {
> >      struct ofputil_bucket *bucket;
> >
> > @@ -2698,6 +2732,7 @@ ofp_print_group(struct ds *s, uint32_t group_id,
> > uint8_t type,
> >          ds_put_cstr(s, "actions=");
> >          struct ofpact_format_params fp = {
> >              .port_map = port_map,
> > +            .table_map = table_map,
> >              .s = s,
> >          };
> >          ofpacts_format(bucket->ofpacts, bucket->ofpacts_len, &fp);
> > @@ -2720,7 +2755,8 @@ ofp_print_ofpst_group_desc_request(struct ds
> > *string,
> >
> >  static enum ofperr
> >  ofp_print_group_desc(struct ds *s, const struct ofp_header *oh,
> > -                     const struct ofputil_port_map *port_map)
> > +                     const struct ofputil_port_map *port_map,
> > +                     const struct ofputil_table_map *table_map)
> >  {
> >      struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
> >      for (;;) {
> > @@ -2735,7 +2771,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, port_map);
> > +                        oh->version, false, port_map, table_map);
> >          ofputil_uninit_group_desc(&gd);
> >       }
> >  }
> > @@ -2842,7 +2878,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_port_map *port_map)
> > +                      const struct ofputil_port_map *port_map,
> > +                      const struct ofputil_table_map *table_map)
> >  {
> >      bool bucket_command = false;
> >
> > @@ -2887,12 +2924,13 @@ 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, port_map);
> > +                    ofp_version, bucket_command, port_map, table_map);
> >  }
> >
> >  static enum ofperr
> >  ofp_print_group_mod(struct ds *s, const struct ofp_header *oh,
> > -                    const struct ofputil_port_map *port_map)
> > +                    const struct ofputil_port_map *port_map,
> > +                    const struct ofputil_table_map *table_map)
> >  {
> >      struct ofputil_group_mod gm;
> >      int error;
> > @@ -2901,7 +2939,7 @@ ofp_print_group_mod(struct ds *s, const struct
> > ofp_header *oh,
> >      if (error) {
> >          return error;
> >      }
> > -    ofp_print_group_mod__(s, oh->version, &gm, port_map);
> > +    ofp_print_group_mod__(s, oh->version, &gm, port_map, table_map);
> >      ofputil_uninit_group_mod(&gm);
> >      return 0;
> >  }
> > @@ -3071,11 +3109,13 @@ ofp_print_table_features(struct ds *s,
> >                           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)
> > +                         const struct ofputil_table_stats *prev_stats,
> > +                         const struct ofputil_table_map *table_map)
> >  {
> >      int i;
> >
> > -    ds_put_format(s, "  table %"PRIu8, features->table_id);
> > +    ds_put_format(s, "  table ");
> > +    ofputil_format_table(features->table_id, table_map, s);
> >      if (features->name[0]) {
> >          ds_put_format(s, " (\"%s\")", features->name);
> >      }
> > @@ -3170,7 +3210,8 @@ ofp_print_table_features(struct ds *s,
> >  }
> >
> >  static enum ofperr
> > -ofp_print_table_features_reply(struct ds *s, const struct ofp_header *oh)
> > +ofp_print_table_features_reply(struct ds *s, const struct ofp_header *oh,
> > +                               const struct ofputil_table_map *table_map)
> >  {
> >      struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
> >
> > @@ -3185,13 +3226,15 @@ ofp_print_table_features_reply(struct ds *s,
> > const struct ofp_header *oh)
> >          }
> >
> >          ds_put_char(s, '\n');
> > -        ofp_print_table_features(s, &tf, i ? &prev : NULL, NULL, NULL);
> > +        ofp_print_table_features(s, &tf, i ? &prev : NULL, NULL, NULL,
> > +                                 table_map);
> >          prev = tf;
> >      }
> >  }
> >
> >  static enum ofperr
> > -ofp_print_table_desc_reply(struct ds *s, const struct ofp_header *oh)
> > +ofp_print_table_desc_reply(struct ds *s, const struct ofp_header *oh,
> > +                           const struct ofputil_table_map *table_map)
> >  {
> >      struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
> >      for (;;) {
> > @@ -3202,7 +3245,7 @@ ofp_print_table_desc_reply(struct ds *s, const
> > struct ofp_header *oh)
> >          if (retval) {
> >              return retval != EOF ? retval : 0;
> >          }
> > -        ofp_print_table_desc(s, &td);
> > +        ofp_print_table_desc(s, &td, table_map);
> >      }
> >  }
> >
> > @@ -3268,7 +3311,9 @@ ofp_print_bundle_ctrl(struct ds *s, const struct
> > ofp_header *oh)
> >
> >  static enum ofperr
> >  ofp_print_bundle_add(struct ds *s, const struct ofp_header *oh,
> > -                     const struct ofputil_port_map *port_map, int
> > verbosity)
> > +                     const struct ofputil_port_map *port_map,
> > +                     const struct ofputil_table_map *table_map,
> > +                     int verbosity)
> >  {
> >      int error;
> >      struct ofputil_bundle_add_msg badd;
> > @@ -3285,7 +3330,7 @@ ofp_print_bundle_add(struct ds *s, const struct
> > ofp_header *oh,
> >
> >      ds_put_char(s, '\n');
> >      char *msg = ofp_to_string(badd.msg, ntohs(badd.msg->length), port_map,
> > -                              verbosity);
> > +                              table_map, verbosity);
> >      ds_put_and_free_cstr(s, msg);
> >
> >      return 0;
> > @@ -3376,7 +3421,8 @@ ofp_print_tlv_table_reply(struct ds *s, const
> > struct ofp_header *oh)
> >   * request forward is taken from rf.request.type */
> >  static enum ofperr
> >  ofp_print_requestforward(struct ds *string, const struct ofp_header *oh,
> > -                         const struct ofputil_port_map *port_map)
> > +                         const struct ofputil_port_map *port_map,
> > +                         const struct ofputil_table_map *table_map)
> >  {
> >      struct ofputil_requestforward rf;
> >      enum ofperr error;
> > @@ -3391,7 +3437,8 @@ 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,
> > port_map);
> > +        ofp_print_group_mod__(string, oh->version, rf.group_mod, port_map,
> > +                              table_map);
> >          break;
> >
> >      case OFPRFR_METER_MOD:
> > @@ -3491,7 +3538,8 @@ ofp_print_nxt_ct_flush_zone(struct ds *string,
> > const struct nx_zone_id *nzi)
> >
> >  static enum ofperr
> >  ofp_to_string__(const struct ofp_header *oh,
> > -                const struct ofputil_port_map *port_map, enum ofpraw raw,
> > +                const struct ofputil_port_map *port_map,
> > +                const struct ofputil_table_map *table_map, enum ofpraw
> > raw,
> >                  struct ds *string, int verbosity)
> >  {
> >      const void *msg = oh;
> > @@ -3509,7 +3557,7 @@ ofp_to_string__(const struct ofp_header *oh,
> >          return ofp_print_ofpst_group_desc_request(string, oh);
> >
> >      case OFPTYPE_GROUP_DESC_STATS_REPLY:
> > -        return ofp_print_group_desc(string, oh, port_map);
> > +        return ofp_print_group_desc(string, oh, port_map, table_map);
> >
> >      case OFPTYPE_GROUP_FEATURES_STATS_REQUEST:
> >          ofp_print_stats(string, oh);
> > @@ -3519,21 +3567,21 @@ ofp_to_string__(const struct ofp_header *oh,
> >          return ofp_print_group_features(string, oh);
> >
> >      case OFPTYPE_GROUP_MOD:
> > -        return ofp_print_group_mod(string, oh, port_map);
> > +        return ofp_print_group_mod(string, oh, port_map, table_map);
> >
> >      case OFPTYPE_TABLE_FEATURES_STATS_REQUEST:
> >      case OFPTYPE_TABLE_FEATURES_STATS_REPLY:
> > -        return ofp_print_table_features_reply(string, oh);
> > +        return ofp_print_table_features_reply(string, oh, table_map);
> >
> >      case OFPTYPE_TABLE_DESC_REQUEST:
> >      case OFPTYPE_TABLE_DESC_REPLY:
> > -        return ofp_print_table_desc_reply(string, oh);
> > +        return ofp_print_table_desc_reply(string, oh, table_map);
> >
> >      case OFPTYPE_HELLO:
> >          return ofp_print_hello(string, oh);
> >
> >      case OFPTYPE_ERROR:
> > -        return ofp_print_error_msg(string, oh, port_map);
> > +        return ofp_print_error_msg(string, oh, port_map, table_map);
> >
> >      case OFPTYPE_ECHO_REQUEST:
> >      case OFPTYPE_ECHO_REPLY:
> > @@ -3555,25 +3603,26 @@ ofp_to_string__(const struct ofp_header *oh,
> >          return ofp_print_set_config(string, oh);
> >
> >      case OFPTYPE_PACKET_IN:
> > -        return ofp_print_packet_in(string, oh, port_map, verbosity);
> > +        return ofp_print_packet_in(string, oh, port_map, table_map,
> > verbosity);
> >
> >      case OFPTYPE_FLOW_REMOVED:
> > -        return ofp_print_flow_removed(string, oh, port_map);
> > +        return ofp_print_flow_removed(string, oh, port_map, table_map);
> >
> >      case OFPTYPE_PORT_STATUS:
> >          return ofp_print_port_status(string, oh);
> >
> >      case OFPTYPE_PACKET_OUT:
> > -        return ofp_print_packet_out(string, oh, port_map, verbosity);
> > +        return ofp_print_packet_out(string, oh, port_map, table_map,
> > +                                    verbosity);
> >
> >      case OFPTYPE_FLOW_MOD:
> > -        return ofp_print_flow_mod(string, oh, port_map, verbosity);
> > +        return ofp_print_flow_mod(string, oh, port_map, table_map,
> > verbosity);
> >
> >      case OFPTYPE_PORT_MOD:
> >          return ofp_print_port_mod(string, oh, port_map);
> >
> >      case OFPTYPE_TABLE_MOD:
> > -        return ofp_print_table_mod(string, oh);
> > +        return ofp_print_table_mod(string, oh, table_map);
> >
> >      case OFPTYPE_METER_MOD:
> >          return ofp_print_meter_mod(string, oh);
> > @@ -3595,10 +3644,10 @@ ofp_to_string__(const struct ofp_header *oh,
> >          return ofp_print_role_status_message(string, oh);
> >
> >      case OFPTYPE_REQUESTFORWARD:
> > -        return ofp_print_requestforward(string, oh, port_map);
> > +        return ofp_print_requestforward(string, oh, port_map, table_map);
> >
> >      case OFPTYPE_TABLE_STATUS:
> > -        return ofp_print_table_status_message(string, oh);
> > +        return ofp_print_table_status_message(string, oh, table_map);
> >
> >      case OFPTYPE_METER_STATS_REQUEST:
> >      case OFPTYPE_METER_CONFIG_STATS_REQUEST:
> > @@ -3625,7 +3674,7 @@ ofp_to_string__(const struct ofp_header *oh,
> >      case OFPTYPE_FLOW_STATS_REQUEST:
> >      case OFPTYPE_AGGREGATE_STATS_REQUEST:
> >          ofp_print_stats(string, oh);
> > -        return ofp_print_flow_stats_request(string, oh, port_map);
> > +        return ofp_print_flow_stats_request(string, oh, port_map,
> > table_map);
> >
> >      case OFPTYPE_TABLE_STATS_REQUEST:
> >          ofp_print_stats(string, oh);
> > @@ -3645,7 +3694,7 @@ ofp_to_string__(const struct ofp_header *oh,
> >
> >      case OFPTYPE_FLOW_STATS_REPLY:
> >          ofp_print_stats(string, oh);
> > -        return ofp_print_flow_stats_reply(string, oh, port_map);
> > +        return ofp_print_flow_stats_reply(string, oh, port_map,
> > table_map);
> >
> >      case OFPTYPE_QUEUE_STATS_REPLY:
> >          ofp_print_stats(string, oh);
> > @@ -3657,7 +3706,7 @@ ofp_to_string__(const struct ofp_header *oh,
> >
> >      case OFPTYPE_TABLE_STATS_REPLY:
> >          ofp_print_stats(string, oh);
> > -        return ofp_print_table_stats_reply(string, oh);
> > +        return ofp_print_table_stats_reply(string, oh, table_map);
> >
> >      case OFPTYPE_AGGREGATE_STATS_REPLY:
> >          ofp_print_stats(string, oh);
> > @@ -3699,16 +3748,19 @@ ofp_to_string__(const struct ofp_header *oh,
> >          break;
> >
> >      case OFPTYPE_FLOW_MONITOR_STATS_REQUEST:
> > -        return ofp_print_nxst_flow_monitor_request(string, msg,
> > port_map);
> > +        return ofp_print_nxst_flow_monitor_request(string, msg, port_map,
> > +                                                   table_map);
> >
> >      case OFPTYPE_FLOW_MONITOR_STATS_REPLY:
> > -        return ofp_print_nxst_flow_monitor_reply(string, msg, port_map);
> > +        return ofp_print_nxst_flow_monitor_reply(string, msg, port_map,
> > +                                                 table_map);
> >
> >      case OFPTYPE_BUNDLE_CONTROL:
> >          return ofp_print_bundle_ctrl(string, msg);
> >
> >      case OFPTYPE_BUNDLE_ADD_MESSAGE:
> > -        return ofp_print_bundle_add(string, msg, port_map, verbosity);
> > +        return ofp_print_bundle_add(string, msg, port_map, table_map,
> > +                                    verbosity);
> >
> >      case OFPTYPE_NXT_TLV_TABLE_MOD:
> >          return ofp_print_tlv_table_mod(string, msg);
> > @@ -3720,7 +3772,8 @@ ofp_to_string__(const struct ofp_header *oh,
> >          return ofp_print_tlv_table_reply(string, msg);
> >
> >      case OFPTYPE_NXT_RESUME:
> > -        return ofp_print_packet_in(string, msg, port_map, verbosity);
> > +        return ofp_print_packet_in(string, msg, port_map, table_map,
> > +                                   verbosity);
> >      case OFPTYPE_IPFIX_BRIDGE_STATS_REQUEST:
> >          break;
> >      case OFPTYPE_IPFIX_BRIDGE_STATS_REPLY:
> > @@ -3751,7 +3804,9 @@ add_newline(struct ds *s)
> >   * for freeing the string. */
> >  char *
> >  ofp_to_string(const void *oh_, size_t len,
> > -              const struct ofputil_port_map *port_map, int verbosity)
> > +              const struct ofputil_port_map *port_map,
> > +              const struct ofputil_table_map *table_map,
> > +              int verbosity)
> >  {
> >      struct ds string = DS_EMPTY_INITIALIZER;
> >      const struct ofp_header *oh = oh_;
> > @@ -3787,7 +3842,8 @@ ofp_to_string(const void *oh_, size_t len,
> >              ofp_header_to_string__(oh, raw, &string);
> >              size_t header_len = string.length;
> >
> > -            error = ofp_to_string__(oh, port_map, raw, &string,
> > verbosity);
> > +            error = ofp_to_string__(oh, port_map, table_map,
> > +                                    raw, &string, verbosity);
> >              if (error) {
> >                  if (string.length > header_len) {
> >                      ds_chomp(&string, ' ');
> > @@ -3827,9 +3883,11 @@ print_and_free(FILE *stream, char *string)
> >   * numbers increase verbosity. */
> >  void
> >  ofp_print(FILE *stream, const void *oh, size_t len,
> > -          const struct ofputil_port_map *port_map, int verbosity)
> > +          const struct ofputil_port_map *port_map,
> > +          const struct ofputil_table_map *table_map, int verbosity)
> >  {
> > -    print_and_free(stream, ofp_to_string(oh, len, port_map, verbosity));
> > +    print_and_free(stream, ofp_to_string(oh, len, port_map, table_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 f3b2e3f6108c..09ad93132918 100644
> > --- a/lib/ofp-util.c
> > +++ b/lib/ofp-util.c
> > @@ -7378,16 +7378,16 @@ ofputil_port_get_reserved_name(ofp_port_t port)
> >      }
> >  }
> >
> > -/* A port name doesn't need to be quoted if it is alphanumeric and starts
> > with
> > - * a letter. */
> > +/* A table or 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)
> > +name_needs_quotes(const char *name)
> >  {
> > -    if (!isalpha((unsigned char) port_name[0])) {
> > +    if (!isalpha((unsigned char) name[0])) {
> >          return true;
> >      }
> >
> > -    for (const char *p = port_name + 1; *p; p++) {
> > +    for (const char *p = name + 1; *p; p++) {
> >          if (!isalnum((unsigned char) *p)) {
> >              return true;
> >          }
> > @@ -7395,13 +7395,14 @@ port_name_needs_quotes(const char *port_name)
> >      return false;
> >  }
> >
> > +/* Appends port or table 'name' to 's', quoting it if necessary. */
> >  static void
> > -put_port_name(const char *port_name, struct ds *s)
> > +put_name(const char *name, struct ds *s)
> >  {
> > -    if (port_name_needs_quotes(port_name)) {
> > -        json_string_escape(port_name, s);
> > +    if (name_needs_quotes(name)) {
> > +        json_string_escape(name, s);
> >      } else {
> > -        ds_put_cstr(s, port_name);
> > +        ds_put_cstr(s, name);
> >      }
> >  }
> >
> > @@ -7420,7 +7421,7 @@ ofputil_format_port(ofp_port_t port, const struct
> > ofputil_port_map *port_map,
> >
> >      const char *port_name = ofputil_port_map_get_name(port_map, port);
> >      if (port_name) {
> > -        put_port_name(port_name, s);
> > +        put_name(port_name, s);
> >          return;
> >      }
> >
> > @@ -7445,7 +7446,7 @@ ofputil_port_to_string(ofp_port_t port,
> >      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);
> > +        put_name(port_name, &s);
> >          ovs_strlcpy(namebuf, ds_cstr(&s), bufsize);
> >          ds_destroy(&s);
> >          return;
> > @@ -7644,6 +7645,92 @@ ofputil_table_map_destroy(struct ofputil_table_map
> > *map)
> >      ofputil_name_map_destroy(&map->map);
> >  }
> >
> > +/* Table numbers. */
> > +
> > +/* Stores the table number represented by 's' into '*tablep'.  's' may be
> > an
> > + * integer or, if 'table_map' is nonnull, a name (quoted or unquoted).
> > + *
> > + * Returns true if successful, false if 's' is not a valid OpenFlow table
> > + * number or name.  The caller should issue an error message in this case,
> > + * because this function usually does not.  (This gives the caller an
> > + * opportunity to look up the table name another way, e.g. by contacting
> > the
> > + * switch and listing the names of all its tables). */
> > +bool
> > +ofputil_table_from_string(const char *s,
> > +                          const struct ofputil_table_map *table_map,
> > +                          uint8_t *tablep)
> > +{
> > +    *tablep = 0;
> > +    if (*s == '-') {
> > +        VLOG_WARN("Negative value %s is not a valid table number.", s);
> > +        return false;
> > +    }
> > +
> > +    unsigned int table;
> > +    if (str_to_uint(s, 10, &table)) {
> > +        if (table > 255) {
> > +            VLOG_WARN("table %u is outside the supported range 0 through
> > 255",
> > +                      table);
> > +            return false;
> > +        }
> > +        *tablep = table;
> > +        return true;
> > +    } else {
> > +        if (s[0] != '"') {
> > +            table = ofputil_table_map_get_number(table_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)) {
> > +                table = ofputil_table_map_get_number(table_map, name);
> > +            }
> > +            free(name);
> > +        }
> > +        if (table != UINT8_MAX) {
> > +            *tablep = table;
> > +            return true;
> > +        }
> > +
> > +        return false;
> > +    }
> > +}
> > +
> > +/* Appends to 's' a string representation of the OpenFlow table number
> > 'table',
> > + * either the table number or a name drawn from 'table_map'. */
> > +void
> > +ofputil_format_table(uint8_t table, const struct ofputil_table_map
> > *table_map,
> > +                     struct ds *s)
> > +{
> > +    const char *table_name = ofputil_table_map_get_name(table_map,
> > table);
> > +    if (table_name) {
> > +        put_name(table_name, s);
> > +    } else {
> > +        ds_put_format(s, "%"PRIu8, table);
> > +    }
> > +}
> > +
> > +/* Puts in the 'bufsize' byte in 'namebuf' a null-terminated string
> > + * representation of OpenFlow table number 'table', either the table's
> > number
> > + * or a name drawn from 'table_map'. */
> > +void
> > +ofputil_table_to_string(uint8_t table,
> > +                        const struct ofputil_table_map *table_map,
> > +                        char *namebuf, size_t bufsize)
> > +{
> > +    const char *table_name = ofputil_table_map_get_name(table_map,
> > table);
> > +    if (table_name) {
> > +        struct ds s = DS_EMPTY_INITIALIZER;
> > +        put_name(table_name, &s);
> > +        ovs_strlcpy(namebuf, ds_cstr(&s), bufsize);
> > +        ds_destroy(&s);
> > +        return;
> > +    }
> > +
> > +    snprintf(namebuf, bufsize, "%"PRIu8, table);
> > +}
> > +
> >  /* 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").
> > diff --git a/lib/vconn.c b/lib/vconn.c
> > index bb56be2d2901..f0d00eec104f 100644
> > --- a/lib/vconn.c
> > +++ b/lib/vconn.c
> > @@ -496,7 +496,7 @@ vcs_recv_hello(struct vconn *vconn)
> >              ofpbuf_delete(b);
> >              return;
> >          } else {
> > -            char *s = ofp_to_string(b->data, b->size, NULL, 1);
> > +            char *s = ofp_to_string(b->data, b->size, NULL, NULL, 1);
> >              VLOG_WARN_RL(&bad_ofmsg_rl,
> >                           "%s: received message while expecting hello: %s",
> >                           vconn->name, s);
> > @@ -642,7 +642,8 @@ 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, NULL,
> > 1);
> > +            char *s = ofp_to_string((*msgp)->data, (*msgp)->size,
> > +                                    NULL, NULL, 1);
> >              VLOG_DBG_RL(&ofmsg_rl, "%s: received: %s", vconn->name, s);
> >              free(s);
> >          }
> > @@ -682,7 +683,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, NULL, 1);
> > +        char *s = ofp_to_string(msg->data, msg->size, NULL, NULL, 1);
> >          retval = (vconn->vclass->send)(vconn, msg);
> >          if (retval != EAGAIN) {
> >              VLOG_DBG_RL(&ofmsg_rl, "%s: sent (%s): %s",
> > @@ -958,7 +959,8 @@ 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,
> > NULL, 1));
> > +                             ofp_to_string(reply->data, reply->size,
> > +                                           NULL, NULL, 1));
> >                  return EPROTO;
> >              }
> >          }
> > diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
> > index 329a775828a1..16aeaec9047e 100644
> > --- a/ofproto/ofproto-dpif.c
> > +++ b/ofproto/ofproto-dpif.c
> > @@ -1734,11 +1734,9 @@ flush(struct ofproto *ofproto_)
> >
> >  static void
> >  query_tables(struct ofproto *ofproto,
> > -             struct ofputil_table_features *features,
> > +             struct ofputil_table_features *features OVS_UNUSED,
> >               struct ofputil_table_stats *stats)
> >  {
> > -    strcpy(features->name, "classifier");
> > -
> >      if (stats) {
> >          int i;
> >
> > diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
> > index 6e360a8fc1e0..02dd12b50f6a 100644
> > --- a/ofproto/ofproto.c
> > +++ b/ofproto/ofproto.c
> > @@ -3221,7 +3221,7 @@ query_tables(struct ofproto *ofproto,
> >          struct ofputil_table_features *f = &features[i];
> >
> >          f->table_id = i;
> > -        sprintf(f->name, "table%d", i);
> > +        f->name[0] = '\0';
> >          f->metadata_match = OVS_BE64_MAX;
> >          f->metadata_write = OVS_BE64_MAX;
> >          atomic_read_relaxed(&ofproto->tables[i].miss_config,
> > &f->miss_config);
> > diff --git a/ovn/controller/ofctrl.c b/ovn/controller/ofctrl.c
> > index b2a6d7f53ff7..f0a0f4aa9885 100644
> > --- a/ovn/controller/ofctrl.c
> > +++ b/ovn/controller/ofctrl.c
> > @@ -294,7 +294,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), NULL, 1);
> > +        char *s = ofp_to_string(oh, ntohs(oh->length), NULL, NULL, 1);
> >          VLOG_ERR("unexpected reply to TLV table request (%s)", s);
> >          free(s);
> >      }
> > @@ -348,7 +348,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), NULL, 1);
> > +        char *s = ofp_to_string(oh, ntohs(oh->length), NULL, NULL, 1);
> >          VLOG_ERR("unexpected reply to Geneve option allocation request
> > (%s)",
> >                   s);
> >          free(s);
> > @@ -533,7 +533,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), NULL, 1);
> > +                char *s = ofp_to_string(oh, ntohs(oh->length), NULL,
> > NULL, 1);
> >                  VLOG_WARN("could not decode OpenFlow message (%s): %s",
> >                            ofperr_to_string(error), s);
> >                  free(s);
> > @@ -593,7 +593,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), NULL, 2);
> > +        char *s = ofp_to_string(oh, ntohs(oh->length), NULL, NULL, 2);
> >          vlog(&this_module, level, "%s: %s", title, s);
> >          free(s);
> >      }
> > @@ -870,7 +870,7 @@ ofctrl_put(struct hmap *flow_table, struct shash
> > *pending_ct_zones,
> >                                         desired->table_id,
> >                                         ds_cstr(&desired->info));
> >          char *error = parse_ofp_group_mod_str(&gm, OFPGC11_ADD,
> > group_string,
> > -                                              NULL, &usable_protocols);
> > +                                              NULL, NULL,
> > &usable_protocols);
> >          if (!error) {
> >              add_group_mod(&gm, &msgs);
> >          } else {
> > @@ -985,7 +985,7 @@ ofctrl_put(struct hmap *flow_table, struct shash
> > *pending_ct_zones,
> >          char *group_string = xasprintf("group_id=%"PRIu32"",
> >                                         installed->table_id);
> >          char *error = parse_ofp_group_mod_str(&gm, OFPGC11_DELETE,
> > -                                              group_string, NULL,
> > +                                              group_string, NULL, NULL,
> >                                                &usable_protocols);
> >          if (!error) {
> >              add_group_mod(&gm, &msgs);
> > diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c
> > index d8bfde09c4cd..faeb3f932350 100644
> > --- a/ovn/controller/pinctrl.c
> > +++ b/ovn/controller/pinctrl.c
> > @@ -1039,7 +1039,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), NULL, 2);
> > +            char *s = ofp_to_string(oh, ntohs(oh->length), NULL, NULL, 2);
> >
> >              VLOG_DBG_RL(&rl, "OpenFlow packet ignored: %s", s);
> >              free(s);
> > diff --git a/ovn/utilities/ovn-sbctl.c b/ovn/utilities/ovn-sbctl.c
> > index 0cb8ea480438..dc56d864c986 100644
> > --- a/ovn/utilities/ovn-sbctl.c
> > +++ b/ovn/utilities/ovn-sbctl.c
> > @@ -796,7 +796,7 @@ sbctl_dump_openflow(struct vconn *vconn, const struct
> > uuid *uuid, bool stats)
> >
> >              ds_clear(&s);
> >              if (stats) {
> > -                ofp_print_flow_stats(&s, fs, NULL, true);
> > +                ofp_print_flow_stats(&s, fs, NULL, NULL, true);
> >              } else {
> >                  ds_put_format(&s, "%stable=%s%"PRIu8" ",
> >                                colors.special, colors.end, fs->table_id);
> > diff --git a/ovn/utilities/ovn-trace.c b/ovn/utilities/ovn-trace.c
> > index 06d4ddf8e2a0..7e88594faa18 100644
> > --- a/ovn/utilities/ovn-trace.c
> > +++ b/ovn/utilities/ovn-trace.c
> > @@ -1936,7 +1936,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], NULL, true);
> > +            ofp_print_flow_stats(&s, &fses[i], NULL, NULL, true);
> >              ovntrace_node_append(super, OVNTRACE_NODE_ACTION,
> >                                   "%s", ds_cstr(&s));
> >          }
> > diff --git a/tests/ofproto.at b/tests/ofproto.at
> > index b49f546afa67..1b0ba94fd837 100644
> > --- a/tests/ofproto.at
> > +++ b/tests/ofproto.at
> > @@ -2174,7 +2174,7 @@ OVS_VSWITCHD_START
> >  # Check the default configuration.
> >  head_table() {
> >      printf 'OFPST_TABLE reply (xid=0x2):
> > -  table 0 ("%s"):
> > +  table 0%s:
> >      active=0, lookup=0, matched=0
> >      max_entries=1000000
> >      matching:
> > @@ -2191,14 +2191,14 @@ head_table() {
> >        tcp_src: exact match or wildcard
> >        tcp_dst: exact match or wildcard
> >
> > -' $1
> > +' "$1"
> >  }
> >  ditto() {
> >      for i in `seq $1 $2`; do
> > -        printf '  table %d ("table%d"): ditto\n' $i $i
> > +        printf '  table %d: ditto\n' $i
> >      done
> >  }
> > -(head_table classifier; ditto 1 253) > expout
> > +(head_table; ditto 1 253) > expout
> >  AT_CHECK([ovs-ofctl dump-tables br0], [0], [expout])
> >  # Change the configuration.
> >  AT_CHECK(
> > @@ -2211,12 +2211,12 @@ AT_CHECK(
> >  <1>
> >  ])
> >  # Check that the configuration was updated.
> > -(head_table main; echo '  table 1 ("table1"):
> > +(head_table ' ("main")'; echo '  table 1:
> >      active=0, lookup=0, matched=0
> >      max_entries=1024
> >      (same matching)
> >
> > -  table 2 ("table2"):
> > +  table 2:
> >      active=0, lookup=0, matched=0
> >      max_entries=1000000
> >      (same matching)
> > @@ -2250,7 +2250,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:
> > -  table 0 ("%s"):
> > +  table 0:
> >      active=0, lookup=0, matched=0
> >      max_entries=1000000
> >      matching:
> > @@ -2267,14 +2267,14 @@ head_table() {
> >        tcp_src: exact match or wildcard
> >        tcp_dst: exact match or wildcard
> >
> > -' $1
> > +'
> >  }
> >  ditto() {
> >      for i in `seq $1 $2`; do
> > -        printf '  table %d ("table%d"): ditto\n' $i $i
> > +        printf '  table %d: ditto\n' $i
> >      done
> >  }
> > -(head_table classifier; ditto 1 253) > expout
> > +(head_table; ditto 1 253) > expout
> >  AT_CHECK([ovs-ofctl dump-tables br0 | strip_xids], [0], [expout])
> >  OVS_VSWITCHD_STOP(["/240\.0\.0\.1/d"])
> >  AT_CLEANUP
> > @@ -2284,7 +2284,7 @@ OVS_VSWITCHD_START
> >  # Check the default configuration.
> >  head_table() {
> >      printf 'OFPST_TABLE reply (OF1.2) (xid=0x2):
> > -  table 0 ("%s"):
> > +  table 0%s:
> >      active=0, lookup=0, matched=0
> >      metadata: match=0xffffffffffffffff write=0xffffffffffffffff
> >      config=controller
> > @@ -2331,15 +2331,15 @@ head_table() {
> >        nd_sll: exact match or wildcard
> >        nd_tll: exact match or wildcard
> >
> > -' $1
> > +' "$1"
> >  }
> >  ditto() {
> >      for i in `seq $1 $2`; do
> > -        printf '  table %d ("table%d"): ditto\n' $i $i
> > +        printf '  table %d: ditto\n' $i
> >      done
> >  }
> >  tail_table() {
> > -    printf '  table 253 ("table253"):
> > +    printf '  table 253:
> >      active=0, lookup=0, matched=0
> >      metadata: match=0xffffffffffffffff write=0xffffffffffffffff
> >      config=controller
> > @@ -2350,7 +2350,7 @@ tail_table() {
> >      (same matching)
> >  '
> >  }
> > -(head_table classifier; ditto 1 252; tail_table) > expout
> > +(head_table; ditto 1 252; tail_table) > expout
> >  AT_CHECK([ovs-ofctl -O OpenFlow12 dump-tables br0], [0], [expout])
> >  # Change the configuration.
> >  AT_CHECK(
> > @@ -2363,7 +2363,7 @@ AT_CHECK(
> >  <1>
> >  ])
> >  # Check that the configuration was updated.
> > -(head_table main; echo '  table 1 ("table1"):
> > +(head_table ' ("main")'; echo '  table 1:
> >      active=0, lookup=0, matched=0
> >      metadata: match=0xffffffffffffffff write=0xffffffffffffffff
> >      config=controller
> > @@ -2371,7 +2371,7 @@ AT_CHECK(
> >      (same instructions)
> >      (same matching)
> >
> > -  table 2 ("table2"):
> > +  table 2:
> >      active=0, lookup=0, matched=0
> >      metadata: match=0xffffffffffffffff write=0xffffffffffffffff
> >      config=controller
> > @@ -2386,7 +2386,7 @@ AT_CLEANUP
> >  AT_SETUP([ofproto - table features (OpenFlow 1.3)])
> >  OVS_VSWITCHD_START
> >  head_table () {
> > -    printf '  table 0 ("%s"):
> > +    printf '  table 0%s:
> >      metadata: match=0xffffffffffffffff write=0xffffffffffffffff
> >      max_entries=1000000
> >      instructions (table miss and others):
> > @@ -2569,10 +2569,10 @@ metadata in_port in_port_oxm pkt_mark ct_mark
> > ct_label reg0 reg1 reg2 reg3 reg4
> >        nsh_c4: arbitrary mask
> >        nsh_ttl: exact match or wildcard
> >
> > -' $1
> > +' "$1"
> >  }
> >  ditto() {
> > -    printf '  table %d ("%s"):
> > +    printf '  table %d:
> >      metadata: match=0xffffffffffffffff write=0xffffffffffffffff
> >      max_entries=%d
> >      instructions (table miss and others):
> > @@ -2581,10 +2581,10 @@ ditto() {
> >        (same actions)
> >      (same matching)
> >
> > -' $1 $2 $3 `expr $1 + 1`
> > +' $1 $2 `expr $1 + 1`
> >  }
> >  tail_tables() {
> > -echo '  table 252 ("table252"):
> > +echo '  table 252:
> >      metadata: match=0xffffffffffffffff write=0xffffffffffffffff
> >      max_entries=1000000
> >      instructions (table miss and others):
> > @@ -2593,7 +2593,7 @@ echo '  table 252 ("table252"):
> >        (same actions)
> >      (same matching)
> >
> > -  table 253 ("table253"):
> > +  table 253:
> >      metadata: match=0xffffffffffffffff write=0xffffffffffffffff
> >      max_entries=1000000
> >      instructions (table miss and others):
> > @@ -2602,9 +2602,9 @@ echo '  table 252 ("table252"):
> >      (same matching)
> >  '
> >  }
> > -(head_table classifier
> > +(head_table
> >   for i in `seq 1 251`; do
> > -     ditto $i table$i 1000000
> > +     ditto $i 1000000
> >   done
> >   tail_tables) > expout
> >  AT_CHECK([ovs-ofctl -O OpenFlow13 dump-table-features br0], [0], [expout])
> > @@ -2619,16 +2619,49 @@ AT_CHECK(
> >  <1>
> >  ])
> >  # Check that the configuration was updated.
> > -(head_table main
> > - ditto 1 table1 1024
> > +(head_table ' ("main")'
> > + ditto 1 1024
> >   for i in `seq 2 251`; do
> > -     ditto $i table$i 1000000
> > +     ditto $i 1000000
> >   done
> >   tail_tables) > expout
> >  AT_CHECK([ovs-ofctl -O OpenFlow13 dump-table-features br0], [0], [expout])
> >  OVS_VSWITCHD_STOP
> >  AT_CLEANUP
> >
> > +AT_SETUP([ofproto - flow table names])
> > +OVS_VSWITCHD_START
> > +add_of_ports br0 1 2
> > +AT_CHECK(
> > +  [ovs-vsctl \
> > +     -- --id=@t0 create Flow_Table name=zero \
> > +     -- --id=@t1 create Flow_Table name=one \
> > +     -- --id=@t2 create Flow_Table name=two \
> > +     -- set bridge br0 'flow_tables={0=@t0,1=@t1,2=@t2}' \
> > +   | uuidfilt],
> > +  [0], [<0>
> > +<1>
> > +<2>
> > +])
> > +AT_DATA([flows.txt], [dnl
> > +table=zero in_port=p2 actions=p1,resubmit(,one)
> > +table=one,in_port=p1,ip,actions=ct(table=two)
> > +table=one,in_port=p1,arp,actions=goto_table(two)
> > +])
> > +AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
> > +AT_CHECK([ovs-ofctl --names --no-stats dump-flows br0], [0], [dnl
> > + table=zero, in_port=p2 actions=output:p1,resubmit(,one)
> > + table=one, ip,in_port=p1 actions=ct(table=two)
> > + table=one, arp,in_port=p1 actions=resubmit(,two)
> > +])
> > +AT_CHECK([ovs-ofctl --no-names --no-stats dump-flows br0], [0], [dnl
> > + in_port=2 actions=output:1,resubmit(,1)
> > + table=1, ip,in_port=1 actions=ct(table=2)
> > + table=1, arp,in_port=1 actions=resubmit(,2)
> > +])
> > +OVS_VSWITCHD_STOP
> > +AT_CLEANUP
> > +
> >  AT_SETUP([ofproto - table description (OpenFlow 1.4)])
> >  OVS_VSWITCHD_START
> >  (x=0
> > diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in
> > index 95344c7e3872..7649bc5c4c50 100644
> > --- a/utilities/ovs-ofctl.8.in
> > +++ b/utilities/ovs-ofctl.8.in
> > @@ -66,9 +66,12 @@ Prints to the console features for each of the flow
> > tables used by
> >  \fBdump\-table\-desc \fIswitch\fR
> >  Prints to the console configuration for each of the flow tables used
> >  by \fIswitch\fR for OpenFlow 1.4+.
> > -.IP "\fBmod\-table \fIswitch\fR \fItable_id\fR \fIsetting\fR"
> > -This command configures flow table settings for OpenFlow table
> > -\fItable_id\fR within \fIswitch\fR.  The available settings depend on
> > +.IP "\fBmod\-table \fIswitch\fR \fItable\fR \fIsetting\fR"
> > +This command configures flow table settings in \fIswitch\fR for
> > +OpenFlow table \fItable\fR, which may be expressed as a number or
> > +(unless \fB\-\-no\-names\fR is specified) a name.
> > +.IP
> > +The available settings depend on
> >  the OpenFlow version in use.  In OpenFlow 1.1 and 1.2 (which must be
> >  enabled with the \fB\-O\fR option) only, \fBmod\-table\fR configures
> >  behavior when no flow is found when a packet is looked up in a flow
> > @@ -606,9 +609,11 @@ connection to the switch.  (These could only occur
> > using the
> >  COMMANDS\fR.)
> >  .IP "\fB!actions\fR"
> >  Do not report actions as part of flow updates.
> > -.IP "\fBtable=\fInumber\fR"
> > -Limits the monitoring to the table with the given \fInumber\fR between
> > -0 and 254.  By default, all tables are monitored.
> > +.IP "\fBtable=\fItable\fR"
> > +Limits the monitoring to the table with the given \fItable\fR, which
> > +may be expressed as a number between 0 and 254 or (unless
> > +\fB\-\-no\-names\fR is specified) a name.  By default, all tables are
> > +monitored.
> >  .IP "\fBout_port=\fIport\fR"
> >  If set, only flows that output to \fIport\fR are monitored.  The
> >  \fIport\fR may be an OpenFlow port number or keyword
> > @@ -699,10 +704,11 @@ flows not in normal form.
> >  them.  In addition to match fields, commands that operate on flows
> >  accept a few additional key-value pairs:
> >  .
> > -.IP \fBtable=\fInumber\fR
> > -For flow dump commands, limits the flows dumped to those in the table
> > -with the given \fInumber\fR between 0 and 254.  If not specified (or if
> > -255 is specified as \fInumber\fR), then flows in all tables are
> > +.IP \fBtable=\fItable\fR
> > +For flow dump commands, limits the flows dumped to those in
> > +\fItable\fR, which may be expressed as a number between 0 and 255 or
> > +(unless \fB\-\-no\-names\fR is specified) a name.  If not specified
> > +(or if 255 is specified as \fItable\fR), then flows in all tables are
> >  dumped.
> >  .
> >  .IP
> > @@ -970,8 +976,8 @@ only known to be implemented by Open vSwitch:
> >  .IP \fBresubmit\fB:\fIport\fR
> >  .IQ \fBresubmit\fB(\fR[\fIport\fR]\fB,\fR[\fItable\fR]\fB)
> >  .IQ \fBresubmit\fB(\fR[\fIport\fR]\fB,\fR[\fItable\fR]\fB,ct)
> > -Re-searches this OpenFlow flow table (or the table whose number is
> > -specified by \fItable\fR) with the \fBin_port\fR field replaced by
> > +Re-searches this OpenFlow flow table (or table \fItable\fR, if
> > +specified) with the \fBin_port\fR field replaced by
> >  \fIport\fR (if \fIport\fR is specified) and the packet 5-tuple fields
> >  swapped with the corresponding conntrack original direction tuple
> >  fields (if \fBct\fR is specified, see \fBct_nw_src\fR above), and
> > @@ -980,6 +986,9 @@ in this flow entry.  The \fBin_port\fR and swapped
> > 5-tuple fields are
> >  restored immediately after the search, before any actions are
> >  executed.
> >  .IP
> > +The \fItable\fR may be expressed as a number between 0 and 254 or
> > +(unless \fB\-\-no\-names\fR is specified) a name.
> > +.IP
> >  The \fBct\fR option requires a valid connection tracking state as a
> >  match prerequisite in the flow where this action is placed.  Examples
> >  of valid connection tracking state matches include
> > @@ -1057,12 +1066,14 @@ existing connection and start a new one in the
> > current direction.
> >  This flag has no effect if the original direction of the connection is
> >  already the same as that of the current packet.
> >  .RE
> > -.IP \fBtable=\fInumber\fR
> > +.IP \fBtable=\fItable\fR
> >  Fork pipeline processing in two. The original instance of the packet will
> >  continue processing the current actions list as an untracked packet. An
> >  additional instance of the packet will be sent to the connection tracker,
> > which
> >  will be re-injected into the OpenFlow pipeline to resume processing in
> > table
> > -\fInumber\fR, with the \fBct_state\fR and other ct match fields set. If
> > the
> > +\fInumber\fR (which may be specified as a number between 0 and 254 or,
> > +unless \fB\-\-no\-names\fR is specified, a name), with the
> > +\fBct_state\fR and other ct match fields set. If
> >  \fBtable\fR is not specified, then the packet which is submitted to the
> >  connection tracker is not re-injected into the OpenFlow pipeline. It is
> >  strongly recommended to specify a table later than the current table to
> > prevent
> > @@ -1472,10 +1483,10 @@ flow syntax.
> >  Adds a \fBfin_timeout\fR action with the specified arguments to the
> >  new flow.  This feature was added in Open vSwitch 1.5.90.
> >  .
> > -.IP \fBtable=\fInumber\fR
> > +.IP \fBtable=\fItable\fR
> >  The table in which the new flow should be inserted.  Specify a decimal
> > -number between 0 and 254.  The default, if \fBtable\fR is unspecified,
> > -is table 1.
> > +number between 0 and 254 or (unless \fB\-\-no\-names\fR is specified)
> > +a name.  The default, if \fBtable\fR is unspecified, is table 1.
> >  .
> >  .IP \fBdelete_learned\fR
> >  This flag enables deletion of the learned flows when the flow with the
> > @@ -1547,12 +1558,6 @@ Add an \fBoutput\fR action to the new flow's
> > actions, that outputs to
> >  the OpenFlow port taken from \fIfield\fB[\fIstart\fB..\fIend\fB]\fR,
> >  which must be an NXM field as described above.
> >  .RE
> > -.IP
> > -For best performance, segregate learned flows into a table (using
> > -\fBtable=\fInumber\fR) that is not used for any other flows except
> > -possibly for a lowest-priority ``catch-all'' flow, that is, a flow
> > -with no match criteria.  (This is why the default \fBtable\fR is 1, to
> > -keep the learned flows separate from the primary flow table 0.)
> >  .RE
> >  .
> >  .RS
> > @@ -1675,7 +1680,9 @@ band type. See the description of the \fBMeter Table
> > Commands\fR, above,
> >  for more details.
> >  .
> >  .IP \fBgoto_table\fR:\fItable\fR
> > -Indicates the next table in the process pipeline.
> > +Jumps to \fItable\Fr as the next table in the process pipeline.  The
> > +\fItable\fR may be a number between 0 and 254 or (unless
> > +\fB\-\-no\-names\fR is specified) a name.
> >  .
> >  .IP "\fBfin_timeout(\fIargument\fR[\fB,\fIargument\fR]\fB)"
> >  This action changes the idle timeout or hard timeout, or both, of this
> > @@ -2286,21 +2293,24 @@ 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
> > +Every OpenFlow port has a name and a number, and every OpenFlow flow
> > +table has a number and sometimes a name.  By default, \fBovs\-ofctl\fR
> > +commands accept both port and table names and numbers, and they
> > +display port and table names if \fBovs\-ofctl\fR is running on an
> > +interactive console, numbers otherwise.  With \fB\-\-names\fR,
> > +\fBovs\-ofctl\fR commands both accept and display port and table
> > +names; with \fB\-\-no\-names\fR, commands neither accept nor display
> > +port and table names.
> > +.IP
> > +If a port or table 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 and table names, \fBovs\-ofctl\fR quotes any name
> > +that does not start with a letter followed by letters or digits.
> > +.IP
> > +Open vSwitch added support for port names and these options.  Open
> > +vSwitch 2.10 added support for table names.  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,
> > @@ -2312,6 +2322,8 @@ 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
> > +OpenFlow and Open vSwitch limit table names to 32 bytes.
> >  .
> >  .IP "\fB\-\-stats\fR"
> >  .IQ "\fB\-\-no\-stats\fR"
> > diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
> > index 53e303400a11..6a30efe8c4c3 100644
> > --- a/utilities/ovs-ofctl.c
> > +++ b/utilities/ovs-ofctl.c
> > @@ -125,15 +125,18 @@ 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;
> > +/* --names, --no-names: Show port and table names in output and accept
> > them in
> > + * input.  (When neither is specified, the default is to accept port
> > names but,
> > + * for backward compatibility, not to show them unless this is an
> > interactive
> > + * console session.)  */
> > +static int use_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 ofputil_table_map *tables_to_accept(
> > +    const char *vconn_name);
> > +static const struct ofputil_table_map *tables_to_show(const char
> > *vconn_name);
> > +static bool should_accept_names(void);
> > +static bool should_show_names(void);
> >
> >  /* --stats, --no-stats: Show statistics in flow dumps? */
> >  static int show_stats = 1;
> > @@ -216,8 +219,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},
> > +        {"names", no_argument, &use_names, 1},
> > +        {"no-names", no_argument, &use_names, 0},
> >          {"stats", no_argument, &show_stats, 1},
> >          {"no-stats", no_argument, &show_stats, 0},
> >          {"unixctl",     required_argument, NULL, OPT_UNIXCTL},
> > @@ -640,7 +643,9 @@ dump_transaction(struct vconn *vconn, struct ofpbuf
> > *request)
> >                  enum ofpraw raw;
> >
> >                  ofp_print(stdout, reply->data, reply->size,
> > -                          ports_to_show(vconn_get_name(vconn)),
> > verbosity + 1);
> > +                          ports_to_show(vconn_get_name(vconn)),
> > +                          tables_to_show(vconn_get_name(vconn)),
> > +                          verbosity + 1);
> >
> >                  ofpraw_decode(&raw, reply->data);
> >                  if (ofptype_from_ofpraw(raw) == OFPTYPE_ERROR) {
> > @@ -652,6 +657,7 @@ dump_transaction(struct vconn *vconn, struct ofpbuf
> > *request)
> >                                ofp_to_string(
> >                                    reply->data, reply->size,
> >                                    ports_to_show(vconn_get_name(vconn)),
> > +                                  tables_to_show(vconn_get_name(vconn)),
> >                                    verbosity + 1));
> >                  }
> >              } else {
> > @@ -666,7 +672,9 @@ 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,
> > -                  ports_to_show(vconn_get_name(vconn)), verbosity + 1);
> > +                  ports_to_show(vconn_get_name(vconn)),
> > +                  tables_to_show(vconn_get_name(vconn)),
> > +                  verbosity + 1);
> >          ofpbuf_delete(reply);
> >      }
> >  }
> > @@ -697,7 +705,9 @@ transact_multiple_noreply(struct vconn *vconn, struct
> > ovs_list *requests)
> >          "talking to %s", vconn_get_name(vconn));
> >      if (reply) {
> >          ofp_print(stderr, reply->data, reply->size,
> > -                  ports_to_show(vconn_get_name(vconn)), verbosity + 2);
> > +                  ports_to_show(vconn_get_name(vconn)),
> > +                  tables_to_show(vconn_get_name(vconn)),
> > +                  verbosity + 2);
> >          exit(1);
> >      }
> >      ofpbuf_delete(reply);
> > @@ -743,7 +753,7 @@ 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, ports_to_show(vconn_name),
> > -                      verbosity + 1);
> > +                      tables_to_show(vconn_name), verbosity + 1);
> >          }
> >          ofpbuf_uninit(&payload);
> >          ofpbuf_delete(error);
> > @@ -823,7 +833,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, NULL, verbosity + 1);
> > +    ofp_print(stdout, reply->data, reply->size, NULL, NULL, verbosity +
> > 1);
> >      ofpbuf_delete(reply);
> >
> >      if (!has_ports) {
> > @@ -884,7 +894,7 @@ 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, NULL,
> > +                ofp_print(stdout, reply->data, reply->size, NULL, NULL,
> >                            verbosity + 1);
> >                  done = true;
> >              } else if (type == OFPTYPE_TABLE_FEATURES_STATS_REPLY) {
> > @@ -904,7 +914,8 @@ ofctl_dump_table_features(struct ovs_cmdl_context
> > *ctx)
> >
> >                      struct ds s = DS_EMPTY_INITIALIZER;
> >                      ofp_print_table_features(&s, &tf, n ? &prev : NULL,
> > -                                             NULL, NULL);
> > +                                             NULL, NULL,
> > +
> >  tables_to_show(ctx->argv[1]));
> >                      puts(ds_cstr(&s));
> >                      ds_destroy(&s);
> >
> > @@ -915,6 +926,7 @@ ofctl_dump_table_features(struct ovs_cmdl_context
> > *ctx)
> >                  ovs_fatal(0, "received bad reply: %s",
> >                            ofp_to_string(reply->data, reply->size,
> >                                          ports_to_show(ctx->argv[1]),
> > +                                        tables_to_show(ctx->argv[1]),
> >                                          verbosity + 1));
> >              }
> >          } else {
> > @@ -1044,7 +1056,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,
> > -                                        NULL, verbosity + 1));
> > +                                        NULL, NULL, verbosity + 1));
> >              }
> >          }
> >
> > @@ -1066,7 +1078,7 @@ port_iterator_next(struct port_iterator *pi, struct
> > ofputil_phy_port *pp)
> >              || type != OFPTYPE_PORT_DESC_STATS_REPLY) {
> >              ovs_fatal(0, "received bad reply: %s",
> >                        ofp_to_string(pi->reply->data, pi->reply->size,
> > NULL,
> > -                                    verbosity + 1));
> > +                                    NULL, verbosity + 1));
> >          }
> >
> >          pi->more = (ofpmp_flags(oh) & OFPSF_REPLY_MORE) != 0;
> > @@ -1163,33 +1175,189 @@ get_port_map(const char *vconn_name)
> >  static const struct ofputil_port_map *
> >  ports_to_accept(const char *vconn_name)
> >  {
> > -    return should_accept_ports() ? get_port_map(vconn_name) : NULL;
> > +    return should_accept_names() ? 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;
> > +    return should_show_names() ? get_port_map(vconn_name) : NULL;
> > +}
> > +
> > +struct table_iterator {
> > +    struct vconn *vconn;
> > +
> > +    enum { TI_STATS, TI_FEATURES } variant;
> > +    struct ofpbuf *reply;
> > +    ovs_be32 send_xid;
> > +    bool more;
> > +
> > +    struct ofputil_table_features features;
> > +};
> > +
> > +/* Initializes 'ti' to prepare for iterating through all of the tables on
> > the
> > + * OpenFlow switch to which 'vconn' is connected.
> > + *
> > + * During iteration, the client should not make other use of 'vconn',
> > because
> > + * that can cause other messages to be interleaved with the replies used
> > by the
> > + * iterator and thus some tables may be missed or a hang can occur. */
> > +static void
> > +table_iterator_init(struct table_iterator *ti, struct vconn *vconn)
> > +{
> > +    memset(ti, 0, sizeof *ti);
> > +    ti->vconn = vconn;
> > +    ti->variant = (vconn_get_version(vconn) < OFP13_VERSION
> > +                   ? TI_STATS : TI_FEATURES);
> > +    ti->more = true;
> > +
> > +    enum ofpraw raw = (ti->variant == TI_STATS
> > +                       ? OFPRAW_OFPST_TABLE_REQUEST
> > +                       : OFPRAW_OFPST13_TABLE_FEATURES_REQUEST);
> > +    struct ofpbuf *rq = ofpraw_alloc(raw, vconn_get_version(vconn), 0);
> > +    ti->send_xid = ((struct ofp_header *) rq->data)->xid;
> > +    send_openflow_buffer(ti->vconn, rq);
> > +}
> > +
> > +/* Obtains the next table from 'ti'.  On success, returns the next table's
> > + * features; on failure, returns NULL.  */
> > +static const struct ofputil_table_features *
> > +table_iterator_next(struct table_iterator *ti)
> > +{
> > +    for (;;) {
> > +        if (ti->reply) {
> > +            int retval;
> > +            if (ti->variant == TI_STATS) {
> > +                struct ofputil_table_stats ts;
> > +                retval = ofputil_decode_table_stats_reply(ti->reply,
> > +                                                          &ts,
> > &ti->features);
> > +            } else {
> > +                ovs_assert(ti->variant == TI_FEATURES);
> > +                retval = ofputil_decode_table_features(ti->reply,
> > +                                                       &ti->features,
> > +                                                       true);
> > +            }
> > +            if (!retval) {
> > +                return &ti->features;
> > +            } else if (retval != EOF) {
> > +                ovs_fatal(0, "received bad reply: %s",
> > +                          ofp_to_string(ti->reply->data, ti->reply->size,
> > +                                        NULL, NULL, verbosity + 1));
> > +            }
> > +        }
> > +
> > +        if (!ti->more) {
> > +            return NULL;
> > +        }
> > +
> > +        ovs_be32 recv_xid;
> > +        do {
> > +            ofpbuf_delete(ti->reply);
> > +            run(vconn_recv_block(ti->vconn, &ti->reply),
> > +                "OpenFlow receive failed");
> > +            recv_xid = ((struct ofp_header *) ti->reply->data)->xid;
> > +        } while (ti->send_xid != recv_xid);
> > +
> > +        struct ofp_header *oh = ti->reply->data;
> > +        enum ofptype type;
> > +        if (ofptype_pull(&type, ti->reply)
> > +            || type != (ti->variant == TI_STATS
> > +                        ? OFPTYPE_TABLE_STATS_REPLY
> > +                        : OFPTYPE_TABLE_FEATURES_STATS_REPLY)) {
> > +            ovs_fatal(0, "received bad reply: %s",
> > +                      ofp_to_string(ti->reply->data, ti->reply->size,
> > NULL,
> > +                                    NULL, verbosity + 1));
> > +        }
> > +
> > +        ti->more = (ofpmp_flags(oh) & OFPSF_REPLY_MORE) != 0;
> > +    }
> > +}
> > +
> > +/* Destroys iterator 'ti'. */
> > +static void
> > +table_iterator_destroy(struct table_iterator *ti)
> > +{
> > +    if (ti) {
> > +        while (ti->more) {
> > +            /* Drain vconn's queue of any other replies for this request.
> > */
> > +            table_iterator_next(ti);
> > +        }
> > +
> > +        ofpbuf_delete(ti->reply);
> > +    }
> > +}
> > +
> > +static const struct ofputil_table_map *
> > +get_table_map(const char *vconn_name)
> > +{
> > +    static struct shash table_maps = SHASH_INITIALIZER(&table_maps);
> > +    struct ofputil_table_map *map = shash_find_data(&table_maps,
> > vconn_name);
> > +    if (!map) {
> > +        map = xmalloc(sizeof *map);
> > +        ofputil_table_map_init(map);
> > +        shash_add(&table_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 table_iterator ti;
> > +            table_iterator_init(&ti, vconn);
> > +            for (;;) {
> > +                const struct ofputil_table_features *tf
> > +                    = table_iterator_next(&ti);
> > +                if (!tf) {
> > +                    break;
> > +                }
> > +                if (tf->name[0]) {
> > +                    ofputil_table_map_put(map, tf->table_id, tf->name);
> > +                }
> > +            }
> > +            table_iterator_destroy(&ti);
> > +
> > +            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_table_map *
> > +tables_to_accept(const char *vconn_name)
> > +{
> > +    return should_accept_names() ? get_table_map(vconn_name) : NULL;
> > +}
> > +
> > +static const struct ofputil_table_map *
> > +tables_to_show(const char *vconn_name)
> > +{
> > +    return should_show_names() ? get_table_map(vconn_name) : NULL;
> >  }
> >
> > -/* We accept port names unless the feature is turned off explicitly. */
> > +/* We accept port and table names unless the feature is turned off
> > + * explicitly. */
> >  static bool
> > -should_accept_ports(void)
> > +should_accept_names(void)
> >  {
> > -    return use_port_names != 0;
> > +    return use_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. */
> > +/* We show port and table 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)
> > +should_show_names(void)
> >  {
> >      static int interactive = -1;
> >      if (interactive == -1) {
> >          interactive = isatty(STDOUT_FILENO);
> >      }
> >
> > -    return use_port_names > 0 || (use_port_names == -1 && interactive);
> > +    return use_names > 0 || (use_names == -1 && interactive);
> >  }
> >
> >  /* Returns the port number corresponding to 'port_name' (which may be a
> > port
> > @@ -1222,7 +1390,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, NULL, 2);
> > +            char *s = ofp_to_string(reply->data, reply->size, NULL, NULL,
> > 2);
> >              VLOG_DBG("%s: failed to set protocol, switch replied: %s",
> >                       vconn_get_name(vconn), s);
> >              free(s);
> > @@ -1274,8 +1442,11 @@ prepare_dump_flows(int argc, char *argv[], bool
> > aggregate,
> >      const char *match = argc > 2 ? argv[2] : "";
> >      const struct ofputil_port_map *port_map
> >          = *match ? ports_to_accept(vconn_name) : NULL;
> > +    const struct ofputil_table_map *table_map
> > +        = *match ? tables_to_accept(vconn_name) : NULL;
> >      error = parse_ofp_flow_stats_request_str(fsr, aggregate, match,
> > -                                             port_map, &usable_protocols);
> > +                                             port_map, table_map,
> > +                                             &usable_protocols);
> >      if (error) {
> >          ovs_fatal(0, "%s", error);
> >      }
> > @@ -1366,7 +1537,7 @@ compare_flows(const void *afs_, const void *bfs_)
> >  static void
> >  ofctl_dump_flows(struct ovs_cmdl_context *ctx)
> >  {
> > -    if (!n_criteria && !should_show_ports() && show_stats) {
> > +    if (!n_criteria && !should_show_names() && show_stats) {
> >          ofctl_dump_flows__(ctx->argc, ctx->argv, false);
> >          return;
> >      } else {
> > @@ -1388,7 +1559,7 @@ ofctl_dump_flows(struct ovs_cmdl_context *ctx)
> >          for (size_t i = 0; i < n_fses; i++) {
> >              ds_clear(&s);
> >              ofp_print_flow_stats(&s, &fses[i],
> > ports_to_show(ctx->argv[1]),
> > -                                 show_stats);
> > +                                 tables_to_show(ctx->argv[1]),
> > show_stats);
> >              printf(" %s\n", ds_cstr(&s));
> >          }
> >          ds_destroy(&s);
> > @@ -1579,7 +1750,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], ports_to_accept(argv[1]),
> > command,
> > +    error = parse_ofp_flow_mod_file(argv[2], ports_to_accept(argv[1]),
> > +                                    tables_to_accept(argv[1]), command,
> >                                      &fms, &n_fms, &usable_protocols);
> >      if (error) {
> >          ovs_fatal(0, "%s", error);
> > @@ -1599,7 +1771,8 @@ ofctl_flow_mod(int argc, char *argv[], uint16_t
> > command)
> >          enum ofputil_protocol usable_protocols;
> >
> >          error = parse_ofp_flow_mod_str(&fm, argc > 2 ? argv[2] : "",
> > -                                       ports_to_accept(argv[1]), command,
> > +                                       ports_to_accept(argv[1]),
> > +                                       tables_to_accept(argv[1]), command,
> >                                         &usable_protocols);
> >          if (error) {
> >              ovs_fatal(0, "%s", error);
> > @@ -1649,7 +1822,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, NULL, 2);
> > +            char *s = ofp_to_string(reply->data, reply->size, NULL, NULL,
> > 2);
> >              VLOG_DBG("%s: failed to set packet in format to nx_packet_in,
> > "
> >                       "controller replied: %s.",
> >                       vconn_get_name(vconn), s);
> > @@ -1745,7 +1918,8 @@ ofctl_send(struct unixctl_conn *conn, int argc,
> >
> >          fprintf(stderr, "send: ");
> >          ofp_print(stderr, msg->data, msg->size,
> > -                  ports_to_show(vconn_get_name(vconn)), verbosity);
> > +                  ports_to_show(vconn_get_name(vconn)),
> > +                  tables_to_show(vconn_get_name(vconn)), verbosity);
> >
> >          error = vconn_send_block(vconn, msg);
> >          if (error) {
> > @@ -1781,7 +1955,7 @@ unixctl_packet_out(struct unixctl_conn *conn, int
> > OVS_UNUSED argc,
> >
> >      error_msg = parse_ofp_packet_out_str(
> >          &po, argv[1], ports_to_accept(vconn_get_name(vconn)),
> > -        &usable_protocols);
> > +        tables_to_accept(vconn_get_name(vconn)), &usable_protocols);
> >      if (error_msg) {
> >          ds_put_format(&reply, "%s\n", error_msg);
> >          free(error_msg);
> > @@ -1797,7 +1971,8 @@ unixctl_packet_out(struct unixctl_conn *conn, int
> > OVS_UNUSED argc,
> >          struct ofpbuf *msg = ofputil_encode_packet_out(&po, protocol);
> >
> >          ofp_print(stderr, msg->data, msg->size,
> > -                  ports_to_show(vconn_get_name(vconn)), verbosity);
> > +                  ports_to_show(vconn_get_name(vconn)),
> > +                  tables_to_show(vconn_get_name(vconn)), verbosity);
> >
> >          int error = vconn_send_block(vconn, msg);
> >          if (error) {
> > @@ -1961,7 +2136,8 @@ monitor_vconn(struct vconn *vconn, bool
> > reply_to_echo_requests,
> >
> >              ofptype_decode(&type, b->data);
> >              ofp_print(stderr, b->data, b->size,
> > -                      ports_to_show(vconn_get_name(vconn)), verbosity +
> > 2);
> > +                      ports_to_show(vconn_get_name(vconn)),
> > +                      tables_to_show(vconn_get_name(vconn)), verbosity +
> > 2);
> >              fflush(stderr);
> >
> >              switch ((int) type) {
> > @@ -2004,6 +2180,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)),
> > +                                  tables_to_show(vconn_get_name(vconn)),
> >                                    verbosity + 2);
> >                          fflush(stderr);
> >
> > @@ -2083,6 +2260,7 @@ ofctl_monitor(struct ovs_cmdl_context *ctx)
> >
> >              error = parse_flow_monitor_request(&fmr, arg + 6,
> >
> > ports_to_accept(ctx->argv[1]),
> > +
> >  tables_to_accept(ctx->argv[1]),
> >                                                 &usable_protocols);
> >              if (error) {
> >                  ovs_fatal(0, "%s", error);
> > @@ -2200,6 +2378,7 @@ ofctl_packet_out(struct ovs_cmdl_context *ctx)
> >          ofpbuf_init(&ofpacts, 64);
> >          struct ofpact_parse_params pp = {
> >              .port_map = ports_to_accept(ctx->argv[1]),
> > +            .table_map = tables_to_accept(ctx->argv[1]),
> >              .ofpacts = &ofpacts,
> >              .usable_protocols = &usable_protocols
> >          };
> > @@ -2237,6 +2416,7 @@ ofctl_packet_out(struct ovs_cmdl_context *ctx)
> >      } else if (ctx->argc == 3) {
> >          error = parse_ofp_packet_out_str(&po, ctx->argv[2],
> >                                           ports_to_accept(ctx->argv[1]),
> > +                                         tables_to_accept(ctx->argv[1]),
> >                                           &usable_protocols);
> >          if (error) {
> >              ovs_fatal(0, "%s", error);
> > @@ -2343,7 +2523,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, NULL,
> > +                          ofp_to_string(reply->data, reply->size, NULL,
> > NULL,
> >                                          verbosity + 1));
> >              }
> >              uint16_t flags = ofpmp_flags(oh);
> > @@ -2386,6 +2566,7 @@ ofctl_mod_table(struct ovs_cmdl_context *ctx)
> >      int i;
> >
> >      error = parse_ofp_table_mod(&tm, ctx->argv[2], ctx->argv[3],
> > +                                tables_to_accept(ctx->argv[1]),
> >                                  &usable_versions);
> >      if (error) {
> >          ovs_fatal(0, "%s", error);
> > @@ -2511,7 +2692,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, NULL, verbosity + 2);
> > +        ofp_print(stdout, b.data, b.size, NULL, NULL, verbosity + 2);
> >      }
> >      ofpbuf_uninit(&b);
> >
> > @@ -2599,7 +2780,7 @@ ofctl_ofp_parse_pcap(struct ovs_cmdl_context *ctx)
> >                             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,
> > -                              NULL, verbosity + 1);
> > +                              NULL, NULL, verbosity + 1);
> >                      dp_packet_pull(payload, length);
> >                  }
> >              }
> > @@ -2644,9 +2825,10 @@ 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, NULL, verbosity +
> > 2);
> > +            ofp_print(stdout, request, request->size, NULL, NULL,
> > +                      verbosity + 2);
> >              printf("Reply:\n");
> > -            ofp_print(stdout, reply, reply->size, NULL, verbosity + 2);
> > +            ofp_print(stdout, reply, reply->size, NULL, NULL, verbosity +
> > 2);
> >          }
> >          printf("%"PRIu32" bytes from %s: xid=%08"PRIx32" time=%.1f ms\n",
> >                 reply->size, ctx->argv[1], ntohl(rpy_hdr->xid),
> > @@ -2809,6 +2991,7 @@ ofctl_group_mod_file(int argc OVS_UNUSED, char
> > *argv[], int command)
> >          command = -2;
> >      }
> >      error = parse_ofp_group_mod_file(argv[2], ports_to_accept(argv[1]),
> > +                                     tables_to_accept(argv[1]),
> >                                       command, &gms, &n_gms,
> > &usable_protocols);
> >      if (error) {
> >          ovs_fatal(0, "%s", error);
> > @@ -2829,6 +3012,7 @@ ofctl_group_mod(int argc, char *argv[], uint16_t
> > command)
> >
> >          error = parse_ofp_group_mod_str(&gm, command, argc > 2 ? argv[2]
> > : "",
> >                                          ports_to_accept(argv[1]),
> > +                                        tables_to_accept(argv[1]),
> >                                          &usable_protocols);
> >          if (error) {
> >              ovs_fatal(0, "%s", error);
> > @@ -2889,6 +3073,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]),
> > +                                    tables_to_accept(ctx->argv[1]),
> >                                      &usable_protocols);
> >      if (error) {
> >          ovs_fatal(0, "%s", error);
> > @@ -2954,6 +3139,7 @@ ofctl_bundle(struct ovs_cmdl_context *ctx)
> >      char *error;
> >
> >      error = parse_ofp_bundle_file(ctx->argv[2],
> > ports_to_accept(ctx->argv[1]),
> > +                                  tables_to_accept(ctx->argv[1]),
> >                                    &bms, &n_bms, &usable_protocols);
> >      if (error) {
> >          ovs_fatal(0, "%s", error);
> > @@ -3126,9 +3312,11 @@ 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.
> > */
> > +    /* Port and table map.  There is only one of each, not one per source,
> > +     * because it only makes sense to display a single name for a given
> > port
> > +     * or table number. */
> >      const struct ofputil_port_map *port_map;
> > +    const struct ofputil_table_map *table_map;
> >  };
> >
> >  /* Frees 'version' and the data that it owns. */
> > @@ -3362,6 +3550,7 @@ fte_state_init(struct fte_state *state)
> >      ovs_list_init(&state->fte_pending_list);
> >      state->tun_tab = NULL;
> >      state->port_map = NULL;
> > +    state->table_map = NULL;
> >  }
> >
> >  static void
> > @@ -3447,7 +3636,7 @@ read_flows_from_file(const char *filename, struct
> > fte_state *state, int index)
> >          enum ofputil_protocol usable;
> >
> >          error = parse_ofp_str(&fm, OFPFC_ADD, ds_cstr(&s),
> > state->port_map,
> > -                              &usable);
> > +                              state->table_map, &usable);
> >          if (error) {
> >              ovs_fatal(0, "%s:%d: %s", filename, line_number, error);
> >          }
> > @@ -3568,6 +3757,7 @@ ofctl_replace_flows(struct ovs_cmdl_context *ctx)
> >
> >      fte_state_init(&fte_state);
> >      fte_state.port_map = ports_to_accept(ctx->argv[1]);
> > +    fte_state.table_map = tables_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);
> > @@ -3819,7 +4009,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, NULL, verbosity);
> > +        ofp_print(stdout, msg->data, msg->size, NULL, NULL, verbosity);
> >          ofpbuf_delete(msg);
> >
> >          free(CONST_CAST(struct ofpact *, fm->ofpacts));
> > @@ -3835,7 +4025,7 @@ 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], NULL,
> > +    error = parse_ofp_flow_mod_str(&fm, ctx->argv[1], NULL, NULL,
> >                                     OFPFC_ADD, &usable_protocols);
> >      if (error) {
> >          ovs_fatal(0, "%s", error);
> > @@ -3853,7 +4043,7 @@ ofctl_parse_flows(struct ovs_cmdl_context *ctx)
> >      size_t n_fms = 0;
> >      char *error;
> >
> > -    error = parse_ofp_flow_mod_file(ctx->argv[1], NULL, OFPFC_ADD,
> > +    error = parse_ofp_flow_mod_file(ctx->argv[1], NULL, NULL, OFPFC_ADD,
> >                                      &fms, &n_fms, &usable_protocols);
> >      if (error) {
> >          ovs_fatal(0, "%s", error);
> > @@ -4295,7 +4485,7 @@ ofctl_check_vlan(struct ovs_cmdl_context *ctx)
> >      string_s = match_to_string(&match, NULL, OFP_DEFAULT_PRIORITY);
> >      printf("%s -> ", string_s);
> >      fflush(stdout);
> > -    error_s = parse_ofp_str(&fm, -1, string_s, NULL, &usable_protocols);
> > +    error_s = parse_ofp_str(&fm, -1, string_s, NULL, NULL,
> > &usable_protocols);
> >      if (error_s) {
> >          ovs_fatal(0, "%s", error_s);
> >      }
> > @@ -4464,7 +4654,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, NULL, verbosity);
> > +    ofp_print(stdout, packet.data, packet.size, NULL, NULL, verbosity);
> >      ofpbuf_uninit(&packet);
> >      ds_destroy(&line);
> >  }
> > @@ -4479,7 +4669,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, NULL, verbosity);
> > +    ofp_print(stdout, hello->data, hello->size, NULL, NULL, verbosity);
> >      ofpbuf_delete(hello);
> >  }
> >
> > diff --git a/utilities/ovs-testcontroller.c b/utilities/ovs-
> > testcontroller.c
> > index 8f8fc2bb13a1..571af0c7f971 100644
> > --- a/utilities/ovs-testcontroller.c
> > +++ b/utilities/ovs-testcontroller.c
> > @@ -335,7 +335,7 @@ parse_options(int argc, char *argv[])
> >              break;
> >
> >          case OPT_WITH_FLOWS:
> > -            error = parse_ofp_flow_mod_file(optarg, NULL, OFPFC_ADD,
> > +            error = parse_ofp_flow_mod_file(optarg, NULL, NULL,
> > OFPFC_ADD,
> >                                              &default_flows,
> > &n_default_flows,
> >                                              &usable_protocols);
> >              if (error) {
> > --
> > 2.15.1
> >
> > _______________________________________________
> > dev mailing list
> > d...@openvswitch.org
> > https://mail.openvswitch.org/mailman/listinfo/ovs-dev
> >
_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to