[ovs-dev,v2,1/3] ofp-actions: Make formatting and parsing functions take a struct argument.

Message ID 20180112205722.25360-2-blp@ovn.org
State Superseded
Headers show
Series
  • support table names in CLI
Related show

Commit Message

Ben Pfaff Jan. 12, 2018, 8:57 p.m.
An upcoming commit will add another parameter for parsing and formatting
actions.  It is much easier to add these parameters if they are
encapsulated in a struct, so this commit first makes that change.

Signed-off-by: Ben Pfaff <blp@ovn.org>
---
 include/openvswitch/ofp-actions.h |   32 +-
 lib/ofp-actions.c                 | 1137 +++++++++++++++----------------------
 lib/ofp-parse.c                   |   24 +-
 lib/ofp-print.c                   |   40 +-
 ofproto/ofproto-dpif-trace.c      |    8 +-
 ofproto/ofproto-dpif-xlate.c      |   12 +-
 ofproto/ofproto.c                 |    3 +-
 ovn/controller/ofctrl.c           |    3 +-
 ovn/utilities/ovn-sbctl.c         |    3 +-
 tests/test-ovn.c                  |    3 +-
 utilities/ovs-ofctl.c             |   19 +-
 11 files changed, 565 insertions(+), 719 deletions(-)

Comments

Yifeng Sun Jan. 30, 2018, 9:18 p.m. | #1
Looks good to me, thanks.

Reviewed-by: Yifeng Sun <pkusunyifeng@gmail.com>


On Fri, Jan 12, 2018 at 12:57 PM, Ben Pfaff <blp@ovn.org> wrote:

> An upcoming commit will add another parameter for parsing and formatting
> actions.  It is much easier to add these parameters if they are
> encapsulated in a struct, so this commit first makes that change.
>
> Signed-off-by: Ben Pfaff <blp@ovn.org>
> ---
>  include/openvswitch/ofp-actions.h |   32 +-
>  lib/ofp-actions.c                 | 1137 +++++++++++++++---------------
> -------
>  lib/ofp-parse.c                   |   24 +-
>  lib/ofp-print.c                   |   40 +-
>  ofproto/ofproto-dpif-trace.c      |    8 +-
>  ofproto/ofproto-dpif-xlate.c      |   12 +-
>  ofproto/ofproto.c                 |    3 +-
>  ovn/controller/ofctrl.c           |    3 +-
>  ovn/utilities/ovn-sbctl.c         |    3 +-
>  tests/test-ovn.c                  |    3 +-
>  utilities/ovs-ofctl.c             |   19 +-
>  11 files changed, 565 insertions(+), 719 deletions(-)
>
> diff --git a/include/openvswitch/ofp-actions.h b/include/openvswitch/ofp-
> actions.h
> index 4e957358f90a..454c705ccf73 100644
> --- a/include/openvswitch/ofp-actions.h
> +++ b/include/openvswitch/ofp-actions.h
> @@ -1064,18 +1064,32 @@ bool ofpacts_equal_stringwise(const struct ofpact
> a[], size_t a_len,
>  const struct mf_field *ofpact_get_mf_dst(const struct ofpact *ofpact);
>  uint32_t ofpacts_get_meter(const struct ofpact[], size_t ofpacts_len);
>
> -/* Formatting and parsing ofpacts. */
> +/* Formatting ofpacts. */
> +struct ofpact_format_params {
> +    /* Input. */
> +    const struct ofputil_port_map *port_map;
> +
> +    /* Output. */
> +    struct ds *s;
> +};
>  void ofpacts_format(const struct ofpact[], size_t ofpacts_len,
> -                    const struct ofputil_port_map *, struct ds *);
> -char *ofpacts_parse_actions(const char *, const struct ofputil_port_map *,
> -                            struct ofpbuf *ofpacts,
> -                            enum ofputil_protocol *usable_protocols)
> +                    const struct ofpact_format_params *);
> +const char *ofpact_name(enum ofpact_type);
> +
> +/* Parsing ofpacts. */
> +struct ofpact_parse_params {
> +    /* Input. */
> +    const struct ofputil_port_map *port_map;
> +
> +    /* Output. */
> +    struct ofpbuf *ofpacts;
> +    enum ofputil_protocol *usable_protocols;
> +};
> +char *ofpacts_parse_actions(const char *, const struct
> ofpact_parse_params *)
>      OVS_WARN_UNUSED_RESULT;
> -char *ofpacts_parse_instructions(const char *, const struct
> ofputil_port_map *,
> -                                 struct ofpbuf *ofpacts,
> -                                 enum ofputil_protocol *usable_protocols)
> +char *ofpacts_parse_instructions(const char *,
> +                                 const struct ofpact_parse_params *)
>      OVS_WARN_UNUSED_RESULT;
> -const char *ofpact_name(enum ofpact_type);
>
>  /* Internal use by the helpers below. */
>  void ofpact_init(struct ofpact *, enum ofpact_type, size_t len);
> diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
> index db933634bf8b..93792ddfca4b 100644
> --- a/lib/ofp-actions.c
> +++ b/lib/ofp-actions.c
> @@ -417,8 +417,7 @@ static void *ofpact_put_raw(struct ofpbuf *, enum
> ofp_version,
>                              enum ofp_raw_action_type, uint64_t arg);
>
>  static char *OVS_WARN_UNUSED_RESULT ofpacts_parse(
> -    char *str, const struct ofputil_port_map *,
> -    struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols,
> +    char *str, const struct ofpact_parse_params *pp,
>      bool allow_instructions, enum ofpact_type outer_action);
>  static enum ofperr ofpacts_pull_openflow_actions__(
>      struct ofpbuf *openflow, unsigned int actions_len,
> @@ -426,8 +425,7 @@ static enum ofperr ofpacts_pull_openflow_actions__(
>      struct ofpbuf *ofpacts, enum ofpact_type outer_action,
>      const struct vl_mff_map *vl_mff_map, uint64_t *ofpacts_tlv_bitmap);
>  static char * OVS_WARN_UNUSED_RESULT ofpacts_parse_copy(
> -    const char *s_, const struct ofputil_port_map *, struct ofpbuf
> *ofpacts,
> -    enum ofputil_protocol *usable_protocols,
> +    const char *s_, const struct ofpact_parse_params *pp,
>      bool allow_instructions, enum ofpact_type outer_action);
>
>  /* Returns the ofpact following 'ofpact', except that if 'ofpact' contains
> @@ -607,16 +605,16 @@ encode_OUTPUT(const struct ofpact_output *output,
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_truncate_subfield(struct ofpact_output_trunc *output_trunc,
> -                        const char *arg_,
> -                        const struct ofputil_port_map *port_map)
> +parse_truncate_subfield(const char *arg_,
> +                        const struct ofpact_parse_params *pp,
> +                        struct ofpact_output_trunc *output_trunc)
>  {
>      char *key, *value;
>      char *arg = CONST_CAST(char *, arg_);
>
>      while (ofputil_parse_key_value(&arg, &key, &value)) {
>          if (!strcmp(key, "port")) {
> -            if (!ofputil_port_from_string(value, port_map,
> +            if (!ofputil_port_from_string(value, pp->port_map,
>                                            &output_trunc->port)) {
>                  return xasprintf("output to unknown truncate port: %s",
>                                    value);
> @@ -643,21 +641,18 @@ parse_truncate_subfield(struct ofpact_output_trunc
> *output_trunc,
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_OUTPUT(const char *arg,
> -             const struct ofputil_port_map *port_map,
> -             struct ofpbuf *ofpacts,
> -             enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +parse_OUTPUT(const char *arg, const struct ofpact_parse_params *pp)
>  {
>      if (strstr(arg, "port") && strstr(arg, "max_len")) {
>          struct ofpact_output_trunc *output_trunc;
>
> -        output_trunc = ofpact_put_OUTPUT_TRUNC(ofpacts);
> -        return parse_truncate_subfield(output_trunc, arg, port_map);
> +        output_trunc = ofpact_put_OUTPUT_TRUNC(pp->ofpacts);
> +        return parse_truncate_subfield(arg, pp, output_trunc);
>      }
>
>      ofp_port_t port;
> -    if (ofputil_port_from_string(arg, port_map, &port)) {
> -        struct ofpact_output *output = ofpact_put_OUTPUT(ofpacts);
> +    if (ofputil_port_from_string(arg, pp->port_map, &port)) {
> +        struct ofpact_output *output = ofpact_put_OUTPUT(pp->ofpacts);
>          output->port = port;
>          output->max_len = output->port == OFPP_CONTROLLER ? UINT16_MAX :
> 0;
>          return NULL;
> @@ -668,7 +663,7 @@ parse_OUTPUT(const char *arg,
>      if (!error) {
>          struct ofpact_output_reg *output_reg;
>
> -        output_reg = ofpact_put_OUTPUT_REG(ofpacts);
> +        output_reg = ofpact_put_OUTPUT_REG(pp->ofpacts);
>          output_reg->max_len = UINT16_MAX;
>          output_reg->src = src;
>          return NULL;
> @@ -680,14 +675,14 @@ parse_OUTPUT(const char *arg,
>
>  static void
>  format_OUTPUT(const struct ofpact_output *a,
> -              const struct ofputil_port_map *port_map, struct ds *s)
> +              const struct ofpact_format_params *fp)
>  {
>      if (ofp_to_u16(a->port) < ofp_to_u16(OFPP_MAX)) {
> -        ds_put_format(s, "%soutput:%s", colors.special, colors.end);
> +        ds_put_format(fp->s, "%soutput:%s", colors.special, colors.end);
>      }
> -    ofputil_format_port(a->port, port_map, s);
> +    ofputil_format_port(a->port, fp->port_map, fp->s);
>      if (a->port == OFPP_CONTROLLER) {
> -        ds_put_format(s, ":%"PRIu16, a->max_len);
> +        ds_put_format(fp->s, ":%"PRIu16, a->max_len);
>      }
>  }
>
> @@ -710,20 +705,16 @@ encode_GROUP(const struct ofpact_group *group,
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_GROUP(char *arg,
> -            const struct ofputil_port_map *port_map OVS_UNUSED,
> -            struct ofpbuf *ofpacts,
> -            enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +parse_GROUP(char *arg, const struct ofpact_parse_params *pp)
>  {
> -    return str_to_u32(arg, &ofpact_put_GROUP(ofpacts)->group_id);
> +    return str_to_u32(arg, &ofpact_put_GROUP(pp->ofpacts)->group_id);
>  }
>
>  static void
>  format_GROUP(const struct ofpact_group *a,
> -             const struct ofputil_port_map *port_map OVS_UNUSED,
> -             struct ds *s)
> +             const struct ofpact_format_params *fp)
>  {
> -    ds_put_format(s, "%sgroup:%s%"PRIu32,
> +    ds_put_format(fp->s, "%sgroup:%s%"PRIu32,
>                    colors.special, colors.end, a->group_id);
>  }
>
> @@ -891,10 +882,7 @@ encode_CONTROLLER(const struct ofpact_controller
> *controller,
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_CONTROLLER(char *arg,
> -                 const struct ofputil_port_map *port_map OVS_UNUSED,
> -                 struct ofpbuf *ofpacts,
> -                 enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +parse_CONTROLLER(char *arg, const struct ofpact_parse_params *pp)
>  {
>      enum ofp_packet_in_reason reason = OFPR_ACTION;
>      uint16_t controller_id = 0;
> @@ -941,30 +929,30 @@ parse_CONTROLLER(char *arg,
>      if (reason == OFPR_ACTION && controller_id == 0 && !userdata &&
> !pause) {
>          struct ofpact_output *output;
>
> -        output = ofpact_put_OUTPUT(ofpacts);
> +        output = ofpact_put_OUTPUT(pp->ofpacts);
>          output->port = OFPP_CONTROLLER;
>          output->max_len = max_len;
>      } else {
>          struct ofpact_controller *controller;
>
> -        controller = ofpact_put_CONTROLLER(ofpacts);
> +        controller = ofpact_put_CONTROLLER(pp->ofpacts);
>          controller->max_len = max_len;
>          controller->reason = reason;
>          controller->controller_id = controller_id;
>          controller->pause = pause;
>
>          if (userdata) {
> -            size_t start_ofs = ofpacts->size;
> -            const char *end = ofpbuf_put_hex(ofpacts, userdata, NULL);
> +            size_t start_ofs = pp->ofpacts->size;
> +            const char *end = ofpbuf_put_hex(pp->ofpacts, userdata, NULL);
>              if (*end) {
>                  return xstrdup("bad hex digit in `controller' "
>                                 "action `userdata'");
>              }
> -            size_t userdata_len = ofpacts->size - start_ofs;
> -            controller = ofpacts->header;
> +            size_t userdata_len = pp->ofpacts->size - start_ofs;
> +            controller = pp->ofpacts->header;
>              controller->userdata_len = userdata_len;
>          }
> -        ofpact_finish_CONTROLLER(ofpacts, &controller);
> +        ofpact_finish_CONTROLLER(pp->ofpacts, &controller);
>      }
>
>      return NULL;
> @@ -983,42 +971,41 @@ format_hex_arg(struct ds *s, const uint8_t *data,
> size_t len)
>
>  static void
>  format_CONTROLLER(const struct ofpact_controller *a,
> -                  const struct ofputil_port_map *port_map OVS_UNUSED,
> -                  struct ds *s)
> +                  const struct ofpact_format_params *fp)
>  {
>      if (a->reason == OFPR_ACTION && !a->controller_id && !a->userdata_len
>          && !a->pause) {
> -        ds_put_format(s, "%sCONTROLLER:%s%"PRIu16,
> +        ds_put_format(fp->s, "%sCONTROLLER:%s%"PRIu16,
>                        colors.special, colors.end, a->max_len);
>      } else {
>          enum ofp_packet_in_reason reason = a->reason;
>
> -        ds_put_format(s, "%scontroller(%s", colors.paren, colors.end);
> +        ds_put_format(fp->s, "%scontroller(%s", colors.paren, colors.end);
>          if (reason != OFPR_ACTION) {
>              char reasonbuf[OFPUTIL_PACKET_IN_REASON_BUFSIZE];
>
> -            ds_put_format(s, "%sreason=%s%s,", colors.param, colors.end,
> +            ds_put_format(fp->s, "%sreason=%s%s,", colors.param,
> colors.end,
>                            ofputil_packet_in_reason_to_string(
>                                reason, reasonbuf, sizeof reasonbuf));
>          }
>          if (a->max_len != UINT16_MAX) {
> -            ds_put_format(s, "%smax_len=%s%"PRIu16",",
> +            ds_put_format(fp->s, "%smax_len=%s%"PRIu16",",
>                            colors.param, colors.end, a->max_len);
>          }
>          if (a->controller_id != 0) {
> -            ds_put_format(s, "%sid=%s%"PRIu16",",
> +            ds_put_format(fp->s, "%sid=%s%"PRIu16",",
>                            colors.param, colors.end, a->controller_id);
>          }
>          if (a->userdata_len) {
> -            ds_put_format(s, "%suserdata=%s", colors.param, colors.end);
> -            format_hex_arg(s, a->userdata, a->userdata_len);
> -            ds_put_char(s, ',');
> +            ds_put_format(fp->s, "%suserdata=%s", colors.param,
> colors.end);
> +            format_hex_arg(fp->s, a->userdata, a->userdata_len);
> +            ds_put_char(fp->s, ',');
>          }
>          if (a->pause) {
> -            ds_put_format(s, "%spause%s,", colors.value, colors.end);
> +            ds_put_format(fp->s, "%spause%s,", colors.value, colors.end);
>          }
> -        ds_chomp(s, ',');
> -        ds_put_format(s, "%s)%s", colors.paren, colors.end);
> +        ds_chomp(fp->s, ',');
> +        ds_put_format(fp->s, "%s)%s", colors.paren, colors.end);
>      }
>  }
>
> @@ -1074,10 +1061,7 @@ encode_ENQUEUE(const struct ofpact_enqueue *enqueue,
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_ENQUEUE(char *arg,
> -              const struct ofputil_port_map *port_map,
> -              struct ofpbuf *ofpacts,
> -              enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +parse_ENQUEUE(char *arg, const struct ofpact_parse_params *pp)
>  {
>      char *sp = NULL;
>      char *port = strtok_r(arg, ":q,", &sp);
> @@ -1089,8 +1073,8 @@ parse_ENQUEUE(char *arg,
>                         "\"enqueue(PORT,QUEUE)\"");
>      }
>
> -    enqueue = ofpact_put_ENQUEUE(ofpacts);
> -    if (!ofputil_port_from_string(port, port_map, &enqueue->port)) {
> +    enqueue = ofpact_put_ENQUEUE(pp->ofpacts);
> +    if (!ofputil_port_from_string(port, pp->port_map, &enqueue->port)) {
>          return xasprintf("%s: enqueue to unknown port", port);
>      }
>      return str_to_u32(queue, &enqueue->queue);
> @@ -1098,11 +1082,11 @@ parse_ENQUEUE(char *arg,
>
>  static void
>  format_ENQUEUE(const struct ofpact_enqueue *a,
> -               const struct ofputil_port_map *port_map, struct ds *s)
> +               const struct ofpact_format_params *fp)
>  {
> -    ds_put_format(s, "%senqueue:%s", colors.param, colors.end);
> -    ofputil_format_port(a->port, port_map, s);
> -    ds_put_format(s, ":%"PRIu32, a->queue);
> +    ds_put_format(fp->s, "%senqueue:%s", colors.param, colors.end);
> +    ofputil_format_port(a->port, fp->port_map, fp->s);
> +    ds_put_format(fp->s, ":%"PRIu32, a->queue);
>  }
>
>  /* Action structure for NXAST_OUTPUT_REG.
> @@ -1245,21 +1229,17 @@ encode_OUTPUT_REG(const struct ofpact_output_reg
> *output_reg,
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_OUTPUT_REG(const char *arg,
> -                 const struct ofputil_port_map *port_map,
> -                 struct ofpbuf *ofpacts,
> -                 enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +parse_OUTPUT_REG(const char *arg, const struct ofpact_parse_params *pp)
>  {
> -    return parse_OUTPUT(arg, port_map, ofpacts, usable_protocols);
> +    return parse_OUTPUT(arg, pp);
>  }
>
>  static void
>  format_OUTPUT_REG(const struct ofpact_output_reg *a,
> -                  const struct ofputil_port_map *port_map OVS_UNUSED,
> -                  struct ds *s)
> +                  const struct ofpact_format_params *fp)
>  {
> -    ds_put_format(s, "%soutput:%s", colors.special, colors.end);
> -    mf_format_subfield(&a->src, s);
> +    ds_put_format(fp->s, "%soutput:%s", colors.special, colors.end);
> +    mf_format_subfield(&a->src, fp->s);
>  }
>
>  /* Action structure for NXAST_BUNDLE and NXAST_BUNDLE_LOAD.
> @@ -1462,25 +1442,22 @@ encode_BUNDLE(const struct ofpact_bundle *bundle,
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_BUNDLE(const char *arg, const struct ofputil_port_map *port_map,
> -             struct ofpbuf *ofpacts,
> -             enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +parse_BUNDLE(const char *arg, const struct ofpact_parse_params *pp)
>  {
> -    return bundle_parse(arg, port_map, ofpacts);
> +    return bundle_parse(arg, pp->port_map, pp->ofpacts);
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_bundle_load(const char *arg, const struct ofputil_port_map
> *port_map,
> -                  struct ofpbuf *ofpacts)
> +parse_bundle_load(const char *arg, const struct ofpact_parse_params *pp)
>  {
> -    return bundle_parse_load(arg, port_map, ofpacts);
> +    return bundle_parse_load(arg, pp->port_map, pp->ofpacts);
>  }
>
>  static void
>  format_BUNDLE(const struct ofpact_bundle *a,
> -              const struct ofputil_port_map *port_map, struct ds *s)
> +              const struct ofpact_format_params *fp)
>  {
> -    bundle_format(a, port_map, s);
> +    bundle_format(a, fp->port_map, fp->s);
>  }
>
>  /* Set VLAN actions. */
> @@ -1538,8 +1515,8 @@ encode_SET_VLAN_VID(const struct ofpact_vlan_vid
> *vlan_vid,
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_set_vlan_vid(char *arg,
> -                   struct ofpbuf *ofpacts, bool push_vlan_if_needed)
> +parse_set_vlan_vid(char *arg, bool push_vlan_if_needed,
> +                   const struct ofpact_parse_params *pp)
>  {
>      struct ofpact_vlan_vid *vlan_vid;
>      uint16_t vid;
> @@ -1553,27 +1530,23 @@ parse_set_vlan_vid(char *arg,
>      if (vid & ~VLAN_VID_MASK) {
>          return xasprintf("%s: not a valid VLAN VID", arg);
>      }
> -    vlan_vid = ofpact_put_SET_VLAN_VID(ofpacts);
> +    vlan_vid = ofpact_put_SET_VLAN_VID(pp->ofpacts);
>      vlan_vid->vlan_vid = vid;
>      vlan_vid->push_vlan_if_needed = push_vlan_if_needed;
>      return NULL;
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_SET_VLAN_VID(char *arg,
> -                   const struct ofputil_port_map *port_map OVS_UNUSED,
> -                   struct ofpbuf *ofpacts,
> -                   enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +parse_SET_VLAN_VID(char *arg, const struct ofpact_parse_params *pp)
>  {
> -    return parse_set_vlan_vid(arg, ofpacts, false);
> +    return parse_set_vlan_vid(arg, false, pp);
>  }
>
>  static void
>  format_SET_VLAN_VID(const struct ofpact_vlan_vid *a,
> -                    const struct ofputil_port_map *port_map OVS_UNUSED,
> -                    struct ds *s)
> +                    const struct ofpact_format_params *fp)
>  {
> -    ds_put_format(s, "%s%s:%s%"PRIu16, colors.param,
> +    ds_put_format(fp->s, "%s%s:%s%"PRIu16, colors.param,
>                    a->push_vlan_if_needed ? "mod_vlan_vid" :
> "set_vlan_vid",
>                    colors.end, a->vlan_vid);
>  }
> @@ -1633,8 +1606,8 @@ encode_SET_VLAN_PCP(const struct ofpact_vlan_pcp
> *vlan_pcp,
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_set_vlan_pcp(char *arg,
> -                   struct ofpbuf *ofpacts, bool push_vlan_if_needed)
> +parse_set_vlan_pcp(char *arg, bool push_vlan_if_needed,
> +                   const struct ofpact_parse_params *pp)
>  {
>      struct ofpact_vlan_pcp *vlan_pcp;
>      uint8_t pcp;
> @@ -1648,27 +1621,23 @@ parse_set_vlan_pcp(char *arg,
>      if (pcp & ~7) {
>          return xasprintf("%s: not a valid VLAN PCP", arg);
>      }
> -    vlan_pcp = ofpact_put_SET_VLAN_PCP(ofpacts);
> +    vlan_pcp = ofpact_put_SET_VLAN_PCP(pp->ofpacts);
>      vlan_pcp->vlan_pcp = pcp;
>      vlan_pcp->push_vlan_if_needed = push_vlan_if_needed;
>      return NULL;
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_SET_VLAN_PCP(char *arg,
> -                   const struct ofputil_port_map *port_map OVS_UNUSED,
> -                   struct ofpbuf *ofpacts,
> -                   enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +parse_SET_VLAN_PCP(char *arg, const struct ofpact_parse_params *pp)
>  {
> -    return parse_set_vlan_pcp(arg, ofpacts, false);
> +    return parse_set_vlan_pcp(arg, false, pp);
>  }
>
>  static void
>  format_SET_VLAN_PCP(const struct ofpact_vlan_pcp *a,
> -                    const struct ofputil_port_map *port_map OVS_UNUSED,
> -                    struct ds *s)
> +                    const struct ofpact_format_params *fp)
>  {
> -    ds_put_format(s, "%s%s:%s%"PRIu8, colors.param,
> +    ds_put_format(fp->s, "%s%s:%s%"PRIu8, colors.param,
>                    a->push_vlan_if_needed ? "mod_vlan_pcp" :
> "set_vlan_pcp",
>                    colors.end, a->vlan_pcp);
>  }
> @@ -1701,28 +1670,24 @@ encode_STRIP_VLAN(const struct ofpact_null *null
> OVS_UNUSED,
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_STRIP_VLAN(char *arg OVS_UNUSED,
> -                 const struct ofputil_port_map *port_map OVS_UNUSED,
> -                 struct ofpbuf *ofpacts,
> -                 enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +parse_STRIP_VLAN(char *arg OVS_UNUSED, const struct ofpact_parse_params
> *pp)
>  {
> -    ofpact_put_STRIP_VLAN(ofpacts)->ofpact.raw = OFPAT_RAW10_STRIP_VLAN;
> +    ofpact_put_STRIP_VLAN(pp->ofpacts)->ofpact.raw =
> OFPAT_RAW10_STRIP_VLAN;
>      return NULL;
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_pop_vlan(struct ofpbuf *ofpacts)
> +parse_pop_vlan(const struct ofpact_parse_params *pp)
>  {
> -    ofpact_put_STRIP_VLAN(ofpacts)->ofpact.raw = OFPAT_RAW11_POP_VLAN;
> +    ofpact_put_STRIP_VLAN(pp->ofpacts)->ofpact.raw =
> OFPAT_RAW11_POP_VLAN;
>      return NULL;
>  }
>
>  static void
>  format_STRIP_VLAN(const struct ofpact_null *a,
> -                  const struct ofputil_port_map *port_map OVS_UNUSED,
> -                  struct ds *s)
> +                  const struct ofpact_format_params *fp)
>  {
> -    ds_put_format(s, (a->ofpact.raw == OFPAT_RAW11_POP_VLAN
> +    ds_put_format(fp->s, (a->ofpact.raw == OFPAT_RAW11_POP_VLAN
>                      ? "%spop_vlan%s"
>                      : "%sstrip_vlan%s"),
>                    colors.value, colors.end);
> @@ -1757,16 +1722,13 @@ encode_PUSH_VLAN(const struct ofpact_push_vlan
> *push_vlan,
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_PUSH_VLAN(char *arg,
> -                const struct ofputil_port_map *port_map OVS_UNUSED,
> -                struct ofpbuf *ofpacts,
> -                enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +parse_PUSH_VLAN(char *arg, const struct ofpact_parse_params *pp)
>  {
>      struct ofpact_push_vlan *push_vlan;
>      uint16_t ethertype;
>      char *error;
>
> -    *usable_protocols &= OFPUTIL_P_OF11_UP;
> +    *pp->usable_protocols &= OFPUTIL_P_OF11_UP;
>      error = str_to_u16(arg, "ethertype", &ethertype);
>      if (error) {
>          return error;
> @@ -1775,17 +1737,16 @@ parse_PUSH_VLAN(char *arg,
>      if (!eth_type_vlan(htons(ethertype))) {
>          return xasprintf("%s: not a valid VLAN ethertype", arg);
>      }
> -    push_vlan = ofpact_put_PUSH_VLAN(ofpacts);
> +    push_vlan = ofpact_put_PUSH_VLAN(pp->ofpacts);
>      push_vlan->ethertype = htons(ethertype);
>      return NULL;
>  }
>
>  static void
>  format_PUSH_VLAN(const struct ofpact_push_vlan *push_vlan,
> -                 const struct ofputil_port_map *port_map OVS_UNUSED,
> -                 struct ds *s)
> +                 const struct ofpact_format_params *fp)
>  {
> -    ds_put_format(s, "%spush_vlan:%s%#"PRIx16,
> +    ds_put_format(fp->s, "%spush_vlan:%s%#"PRIx16,
>                    colors.param, colors.end, ntohs(push_vlan->ethertype));
>  }
>
> @@ -1851,38 +1812,30 @@ encode_SET_ETH_DST(const struct ofpact_mac *mac,
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_SET_ETH_SRC(char *arg,
> -                  const struct ofputil_port_map *port_map OVS_UNUSED,
> -                  struct ofpbuf *ofpacts,
> -                  enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +parse_SET_ETH_SRC(char *arg, const struct ofpact_parse_params *pp)
>  {
> -    return str_to_mac(arg, &ofpact_put_SET_ETH_SRC(ofpacts)->mac);
> +    return str_to_mac(arg, &ofpact_put_SET_ETH_SRC(pp->ofpacts)->mac);
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_SET_ETH_DST(char *arg,
> -                  const struct ofputil_port_map *port_map OVS_UNUSED,
> -                  struct ofpbuf *ofpacts,
> -                  enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +parse_SET_ETH_DST(char *arg, const struct ofpact_parse_params *pp)
>  {
> -    return str_to_mac(arg, &ofpact_put_SET_ETH_DST(ofpacts)->mac);
> +    return str_to_mac(arg, &ofpact_put_SET_ETH_DST(pp->ofpacts)->mac);
>  }
>
>  static void
>  format_SET_ETH_SRC(const struct ofpact_mac *a,
> -                   const struct ofputil_port_map *port_map OVS_UNUSED,
> -                   struct ds *s)
> +                   const struct ofpact_format_params *fp)
>  {
> -    ds_put_format(s, "%smod_dl_src:%s"ETH_ADDR_FMT,
> +    ds_put_format(fp->s, "%smod_dl_src:%s"ETH_ADDR_FMT,
>                    colors.param, colors.end, ETH_ADDR_ARGS(a->mac));
>  }
>
>  static void
>  format_SET_ETH_DST(const struct ofpact_mac *a,
> -                   const struct ofputil_port_map *port_map OVS_UNUSED,
> -                   struct ds *s)
> +                   const struct ofpact_format_params *fp)
>  {
> -    ds_put_format(s, "%smod_dl_dst:%s"ETH_ADDR_FMT,
> +    ds_put_format(fp->s, "%smod_dl_dst:%s"ETH_ADDR_FMT,
>                    colors.param, colors.end, ETH_ADDR_ARGS(a->mac));
>  }
>
> @@ -1937,38 +1890,30 @@ encode_SET_IPV4_DST(const struct ofpact_ipv4 *ipv4,
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_SET_IPV4_SRC(char *arg,
> -                   const struct ofputil_port_map *port_map OVS_UNUSED,
> -                   struct ofpbuf *ofpacts,
> -                   enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +parse_SET_IPV4_SRC(char *arg, const struct ofpact_parse_params *pp)
>  {
> -    return str_to_ip(arg, &ofpact_put_SET_IPV4_SRC(ofpacts)->ipv4);
> +    return str_to_ip(arg, &ofpact_put_SET_IPV4_SRC(pp->ofpacts)->ipv4);
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_SET_IPV4_DST(char *arg,
> -                   const struct ofputil_port_map *port_map OVS_UNUSED,
> -                   struct ofpbuf *ofpacts,
> -                   enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +parse_SET_IPV4_DST(char *arg, const struct ofpact_parse_params *pp)
>  {
> -    return str_to_ip(arg, &ofpact_put_SET_IPV4_DST(ofpacts)->ipv4);
> +    return str_to_ip(arg, &ofpact_put_SET_IPV4_DST(pp->ofpacts)->ipv4);
>  }
>
>  static void
>  format_SET_IPV4_SRC(const struct ofpact_ipv4 *a,
> -                    const struct ofputil_port_map *port_map OVS_UNUSED,
> -                    struct ds *s)
> +                    const struct ofpact_format_params *fp)
>  {
> -    ds_put_format(s, "%smod_nw_src:%s"IP_FMT,
> +    ds_put_format(fp->s, "%smod_nw_src:%s"IP_FMT,
>                    colors.param, colors.end, IP_ARGS(a->ipv4));
>  }
>
>  static void
>  format_SET_IPV4_DST(const struct ofpact_ipv4 *a,
> -                    const struct ofputil_port_map *port_map OVS_UNUSED,
> -                    struct ds *s)
> +                    const struct ofpact_format_params *fp)
>  {
> -    ds_put_format(s, "%smod_nw_dst:%s"IP_FMT,
> +    ds_put_format(fp->s, "%smod_nw_dst:%s"IP_FMT,
>                    colors.param, colors.end, IP_ARGS(a->ipv4));
>  }
>
> @@ -1999,10 +1944,8 @@ encode_SET_IP_DSCP(const struct ofpact_dscp *dscp,
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_SET_IP_DSCP(char *arg,
> -                  const struct ofputil_port_map *port_map OVS_UNUSED,
> -                  struct ofpbuf *ofpacts,
> -                  enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +parse_SET_IP_DSCP(char *arg, const struct ofpact_parse_params *pp)
> +
>  {
>      uint8_t tos;
>      char *error;
> @@ -2015,16 +1958,16 @@ parse_SET_IP_DSCP(char *arg,
>      if (tos & ~IP_DSCP_MASK) {
>          return xasprintf("%s: not a valid TOS", arg);
>      }
> -    ofpact_put_SET_IP_DSCP(ofpacts)->dscp = tos;
> +    ofpact_put_SET_IP_DSCP(pp->ofpacts)->dscp = tos;
>      return NULL;
>  }
>
>  static void
>  format_SET_IP_DSCP(const struct ofpact_dscp *a,
> -                   const struct ofputil_port_map *port_map OVS_UNUSED,
> -                   struct ds *s)
> +                   const struct ofpact_format_params *fp)
>  {
> -    ds_put_format(s, "%smod_nw_tos:%s%d", colors.param, colors.end,
> a->dscp);
> +    ds_put_format(fp->s, "%smod_nw_tos:%s%d",
> +                  colors.param, colors.end, a->dscp);
>  }
>
>  /* Set IPv4/v6 ECN actions. */
> @@ -2059,10 +2002,7 @@ encode_SET_IP_ECN(const struct ofpact_ecn *ip_ecn,
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_SET_IP_ECN(char *arg,
> -                 const struct ofputil_port_map *port_map OVS_UNUSED,
> -                 struct ofpbuf *ofpacts,
> -                 enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +parse_SET_IP_ECN(char *arg, const struct ofpact_parse_params *pp)
>  {
>      uint8_t ecn;
>      char *error;
> @@ -2075,16 +2015,15 @@ parse_SET_IP_ECN(char *arg,
>      if (ecn & ~IP_ECN_MASK) {
>          return xasprintf("%s: not a valid ECN", arg);
>      }
> -    ofpact_put_SET_IP_ECN(ofpacts)->ecn = ecn;
> +    ofpact_put_SET_IP_ECN(pp->ofpacts)->ecn = ecn;
>      return NULL;
>  }
>
>  static void
>  format_SET_IP_ECN(const struct ofpact_ecn *a,
> -                  const struct ofputil_port_map *port_map OVS_UNUSED,
> -                  struct ds *s)
> +                  const struct ofpact_format_params *fp)
>  {
> -    ds_put_format(s, "%smod_nw_ecn:%s%d",
> +    ds_put_format(fp->s, "%smod_nw_ecn:%s%d",
>                    colors.param, colors.end, a->ecn);
>  }
>
> @@ -2113,10 +2052,8 @@ encode_SET_IP_TTL(const struct ofpact_ip_ttl *ttl,
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_SET_IP_TTL(char *arg,
> -                 const struct ofputil_port_map *port_map OVS_UNUSED,
> -                 struct ofpbuf *ofpacts,
> -                 enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +parse_SET_IP_TTL(char *arg, const struct ofpact_parse_params *pp)
> +
>  {
>      uint8_t ttl;
>      char *error;
> @@ -2126,16 +2063,16 @@ parse_SET_IP_TTL(char *arg,
>          return error;
>      }
>
> -    ofpact_put_SET_IP_TTL(ofpacts)->ttl = ttl;
> +    ofpact_put_SET_IP_TTL(pp->ofpacts)->ttl = ttl;
>      return NULL;
>  }
>
>  static void
>  format_SET_IP_TTL(const struct ofpact_ip_ttl *a,
> -                  const struct ofputil_port_map *port_map OVS_UNUSED,
> -                  struct ds *s)
> +                  const struct ofpact_format_params *fp)
>  {
> -    ds_put_format(s, "%smod_nw_ttl:%s%d", colors.param, colors.end,
> a->ttl);
> +    ds_put_format(fp->s, "%smod_nw_ttl:%s%d",
> +                  colors.param, colors.end, a->ttl);
>  }
>
>  /* Set TCP/UDP/SCTP port actions. */
> @@ -2200,39 +2137,33 @@ encode_SET_L4_DST_PORT(const struct ofpact_l4_port
> *l4_port,
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_SET_L4_SRC_PORT(char *arg,
> -                      const struct ofputil_port_map *port_map OVS_UNUSED,
> -                      struct ofpbuf *ofpacts,
> -                      enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +parse_SET_L4_SRC_PORT(char *arg, const struct ofpact_parse_params *pp)
>  {
>      return str_to_u16(arg, "source port",
> -                      &ofpact_put_SET_L4_SRC_PORT(ofpacts)->port);
> +                      &ofpact_put_SET_L4_SRC_PORT(pp->ofpacts)->port);
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_SET_L4_DST_PORT(char *arg,
> -                      const struct ofputil_port_map *port_map OVS_UNUSED,
> -                      struct ofpbuf *ofpacts,
> -                      enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +parse_SET_L4_DST_PORT(char *arg, const struct ofpact_parse_params *pp)
>  {
>      return str_to_u16(arg, "destination port",
> -                      &ofpact_put_SET_L4_DST_PORT(ofpacts)->port);
> +                      &ofpact_put_SET_L4_DST_PORT(pp->ofpacts)->port);
>  }
>
>  static void
>  format_SET_L4_SRC_PORT(const struct ofpact_l4_port *a,
> -                       const struct ofputil_port_map *port_map OVS_UNUSED,
> -                       struct ds *s)
> +                       const struct ofpact_format_params *fp)
>  {
> -    ds_put_format(s, "%smod_tp_src:%s%d", colors.param, colors.end,
> a->port);
> +    ds_put_format(fp->s, "%smod_tp_src:%s%d",
> +                  colors.param, colors.end, a->port);
>  }
>
>  static void
>  format_SET_L4_DST_PORT(const struct ofpact_l4_port *a,
> -                       const struct ofputil_port_map *port_map OVS_UNUSED,
> -                       struct ds *s)
> +                       const struct ofpact_format_params *fp)
>  {
> -    ds_put_format(s, "%smod_tp_dst:%s%d", colors.param, colors.end,
> a->port);
> +    ds_put_format(fp->s, "%smod_tp_dst:%s%d",
> +                  colors.param, colors.end, a->port);
>  }
>
>  /* Action structure for OFPAT_COPY_FIELD. */
> @@ -2520,21 +2451,17 @@ encode_REG_MOVE(const struct ofpact_reg_move *move,
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_REG_MOVE(const char *arg,
> -               const struct ofputil_port_map *port_map OVS_UNUSED,
> -               struct ofpbuf *ofpacts,
> -               enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +parse_REG_MOVE(const char *arg, const struct ofpact_parse_params *pp)
>  {
> -    struct ofpact_reg_move *move = ofpact_put_REG_MOVE(ofpacts);
> +    struct ofpact_reg_move *move = ofpact_put_REG_MOVE(pp->ofpacts);
>      return nxm_parse_reg_move(move, arg);
>  }
>
>  static void
>  format_REG_MOVE(const struct ofpact_reg_move *a,
> -                const struct ofputil_port_map *port_map OVS_UNUSED,
> -                struct ds *s)
> +                const struct ofpact_format_params *fp)
>  {
> -    nxm_format_reg_move(a, s);
> +    nxm_format_reg_move(a, fp->s);
>  }
>
>  /* Action structure for OFPAT12_SET_FIELD. */
> @@ -3027,14 +2954,12 @@ set_field_split_str(char *arg, char **key, char
> **value, char **delim)
>  }
>
>  /* Parses a "set_field" action with argument 'arg', appending the parsed
> - * action to 'ofpacts'.
> + * action to 'pp->ofpacts'.
>   *
>   * Returns NULL if successful, otherwise a malloc()'d string describing
> the
>   * error.  The caller is responsible for freeing the returned string. */
>  static char * OVS_WARN_UNUSED_RESULT
> -set_field_parse__(char *arg, const struct ofputil_port_map *port_map,
> -                  struct ofpbuf *ofpacts,
> -                  enum ofputil_protocol *usable_protocols)
> +set_field_parse__(char *arg, const struct ofpact_parse_params *pp)
>  {
>      char *value;
>      char *delim;
> @@ -3057,7 +2982,7 @@ set_field_parse__(char *arg, const struct
> ofputil_port_map *port_map,
>      }
>
>      delim[0] = '\0';
> -    error = mf_parse(mf, value, port_map, &sf_value, &sf_mask);
> +    error = mf_parse(mf, value, pp->port_map, &sf_value, &sf_mask);
>      if (error) {
>          return error;
>      }
> @@ -3066,30 +2991,28 @@ set_field_parse__(char *arg, const struct
> ofputil_port_map *port_map,
>          return xasprintf("%s is not a valid value for field %s", value,
> key);
>      }
>
> -    *usable_protocols &= mf->usable_protocols_exact;
> +    *pp->usable_protocols &= mf->usable_protocols_exact;
>
> -    ofpact_put_set_field(ofpacts, mf, &sf_value, &sf_mask);
> +    ofpact_put_set_field(pp->ofpacts, mf, &sf_value, &sf_mask);
>      return NULL;
>  }
>
>  /* Parses 'arg' as the argument to a "set_field" action, and appends such
> an
> - * action to 'ofpacts'.
> + * action to 'pp->ofpacts'.
>   *
>   * Returns NULL if successful, otherwise a malloc()'d string describing
> the
>   * error.  The caller is responsible for freeing the returned string. */
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_SET_FIELD(const char *arg, const struct ofputil_port_map *port_map,
> -                struct ofpbuf *ofpacts,
> -                enum ofputil_protocol *usable_protocols)
> +parse_SET_FIELD(const char *arg, const struct ofpact_parse_params *pp)
>  {
>      char *copy = xstrdup(arg);
> -    char *error = set_field_parse__(copy, port_map, ofpacts,
> usable_protocols);
> +    char *error = set_field_parse__(copy, pp);
>      free(copy);
>      return error;
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_reg_load(char *arg, struct ofpbuf *ofpacts)
> +parse_reg_load(char *arg, const struct ofpact_parse_params *pp)
>  {
>      struct mf_subfield dst;
>      char *key, *value_str;
> @@ -3123,8 +3046,8 @@ parse_reg_load(char *arg, struct ofpbuf *ofpacts)
>          return error;
>      }
>
> -    struct ofpact_set_field *sf = ofpact_put_reg_load(ofpacts, dst.field,
> NULL,
> -                                                      NULL);
> +    struct ofpact_set_field *sf = ofpact_put_reg_load(pp->ofpacts,
> dst.field,
> +                                                      NULL, NULL);
>
>      bitwise_copy(&value, dst.field->n_bytes, 0, sf->value,
>                   dst.field->n_bytes, dst.ofs, dst.n_bits);
> @@ -3135,8 +3058,7 @@ parse_reg_load(char *arg, struct ofpbuf *ofpacts)
>
>  static void
>  format_SET_FIELD(const struct ofpact_set_field *a,
> -                 const struct ofputil_port_map *port_map,
> -                 struct ds *s)
> +                 const struct ofpact_format_params *fp)
>  {
>      if (a->ofpact.raw == NXAST_RAW_REG_LOAD) {
>          struct mf_subfield dst;
> @@ -3144,17 +3066,18 @@ format_SET_FIELD(const struct ofpact_set_field *a,
>
>          dst.ofs = dst.n_bits = 0;
>          while (next_load_segment(a, &dst, &value)) {
> -            ds_put_format(s, "%sload:%s%#"PRIx64"%s->%s",
> +            ds_put_format(fp->s, "%sload:%s%#"PRIx64"%s->%s",
>                            colors.special, colors.end, value,
>                            colors.special, colors.end);
> -            mf_format_subfield(&dst, s);
> -            ds_put_char(s, ',');
> +            mf_format_subfield(&dst, fp->s);
> +            ds_put_char(fp->s, ',');
>          }
> -        ds_chomp(s, ',');
> +        ds_chomp(fp->s, ',');
>      } else {
> -        ds_put_format(s, "%sset_field:%s", colors.special, colors.end);
> -        mf_format(a->field, a->value, ofpact_set_field_mask(a), port_map,
> s);
> -        ds_put_format(s, "%s->%s%s",
> +        ds_put_format(fp->s, "%sset_field:%s", colors.special,
> colors.end);
> +        mf_format(a->field, a->value, ofpact_set_field_mask(a),
> +                  fp->port_map, fp->s);
> +        ds_put_format(fp->s, "%s->%s%s",
>                        colors.special, colors.end, a->field->name);
>      }
>  }
> @@ -3319,37 +3242,29 @@ encode_STACK_POP(const struct ofpact_stack *stack,
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_STACK_PUSH(char *arg,
> -                 const struct ofputil_port_map *port_map OVS_UNUSED,
> -                 struct ofpbuf *ofpacts,
> -                 enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +parse_STACK_PUSH(char *arg, const struct ofpact_parse_params *pp)
>  {
> -    return nxm_parse_stack_action(ofpact_put_STACK_PUSH(ofpacts), arg);
> +    return nxm_parse_stack_action(ofpact_put_STACK_PUSH(pp->ofpacts),
> arg);
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_STACK_POP(char *arg,
> -                const struct ofputil_port_map *port_map OVS_UNUSED,
> -                struct ofpbuf *ofpacts,
> -                enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +parse_STACK_POP(char *arg, const struct ofpact_parse_params *pp)
>  {
> -    return nxm_parse_stack_action(ofpact_put_STACK_POP(ofpacts), arg);
> +    return nxm_parse_stack_action(ofpact_put_STACK_POP(pp->ofpacts),
> arg);
>  }
>
>  static void
>  format_STACK_PUSH(const struct ofpact_stack *a,
> -                  const struct ofputil_port_map *port_map OVS_UNUSED,
> -                  struct ds *s)
> +                  const struct ofpact_format_params *fp)
>  {
> -    nxm_format_stack_push(a, s);
> +    nxm_format_stack_push(a, fp->s);
>  }
>
>  static void
>  format_STACK_POP(const struct ofpact_stack *a,
> -                 const struct ofputil_port_map *port_map OVS_UNUSED,
> -                 struct ds *s)
> +                 const struct ofpact_format_params *fp)
>  {
> -    nxm_format_stack_pop(a, s);
> +    nxm_format_stack_pop(a, fp->s);
>  }
>
>  /* Action structure for NXAST_DEC_TTL_CNT_IDS.
> @@ -3457,66 +3372,62 @@ encode_DEC_TTL(const struct ofpact_cnt_ids
> *dec_ttl,
>  }
>
>  static void
> -parse_noargs_dec_ttl(struct ofpbuf *ofpacts)
> +parse_noargs_dec_ttl(const struct ofpact_parse_params *pp)
>  {
>      struct ofpact_cnt_ids *ids;
>      uint16_t id = 0;
>
> -    ofpact_put_DEC_TTL(ofpacts);
> -    ofpbuf_put(ofpacts, &id, sizeof id);
> -    ids = ofpacts->header;
> +    ofpact_put_DEC_TTL(pp->ofpacts);
> +    ofpbuf_put(pp->ofpacts, &id, sizeof id);
> +    ids = pp->ofpacts->header;
>      ids->n_controllers++;
> -    ofpact_finish_DEC_TTL(ofpacts, &ids);
> +    ofpact_finish_DEC_TTL(pp->ofpacts, &ids);
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_DEC_TTL(char *arg,
> -              const struct ofputil_port_map *port_map OVS_UNUSED,
> -              struct ofpbuf *ofpacts,
> -              enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +parse_DEC_TTL(char *arg, const struct ofpact_parse_params *pp)
>  {
>      if (*arg == '\0') {
> -        parse_noargs_dec_ttl(ofpacts);
> +        parse_noargs_dec_ttl(pp);
>      } else {
>          struct ofpact_cnt_ids *ids;
>          char *cntr;
>
> -        ids = ofpact_put_DEC_TTL(ofpacts);
> +        ids = ofpact_put_DEC_TTL(pp->ofpacts);
>          ids->ofpact.raw = NXAST_RAW_DEC_TTL_CNT_IDS;
>          for (cntr = strtok_r(arg, ", ", &arg); cntr != NULL;
>               cntr = strtok_r(NULL, ", ", &arg)) {
>              uint16_t id = atoi(cntr);
>
> -            ofpbuf_put(ofpacts, &id, sizeof id);
> -            ids = ofpacts->header;
> +            ofpbuf_put(pp->ofpacts, &id, sizeof id);
> +            ids = pp->ofpacts->header;
>              ids->n_controllers++;
>          }
>          if (!ids->n_controllers) {
>              return xstrdup("dec_ttl_cnt_ids: expected at least one
> controller "
>                             "id.");
>          }
> -        ofpact_finish_DEC_TTL(ofpacts, &ids);
> +        ofpact_finish_DEC_TTL(pp->ofpacts, &ids);
>      }
>      return NULL;
>  }
>
>  static void
>  format_DEC_TTL(const struct ofpact_cnt_ids *a,
> -               const struct ofputil_port_map *port_map OVS_UNUSED,
> -               struct ds *s)
> +               const struct ofpact_format_params *fp)
>  {
>      size_t i;
>
> -    ds_put_format(s, "%sdec_ttl%s", colors.paren, colors.end);
> +    ds_put_format(fp->s, "%sdec_ttl%s", colors.paren, colors.end);
>      if (a->ofpact.raw == NXAST_RAW_DEC_TTL_CNT_IDS) {
> -        ds_put_format(s, "%s(%s", colors.paren, colors.end);
> +        ds_put_format(fp->s, "%s(%s", colors.paren, colors.end);
>          for (i = 0; i < a->n_controllers; i++) {
>              if (i) {
> -                ds_put_cstr(s, ",");
> +                ds_put_cstr(fp->s, ",");
>              }
> -            ds_put_format(s, "%"PRIu16, a->cnt_ids[i]);
> +            ds_put_format(fp->s, "%"PRIu16, a->cnt_ids[i]);
>          }
> -        ds_put_format(s, "%s)%s", colors.paren, colors.end);
> +        ds_put_format(fp->s, "%s)%s", colors.paren, colors.end);
>      }
>  }
>
> @@ -3544,12 +3455,10 @@ encode_SET_MPLS_LABEL(const struct
> ofpact_mpls_label *label,
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_SET_MPLS_LABEL(char *arg,
> -                     const struct ofputil_port_map *port_map OVS_UNUSED,
> -                     struct ofpbuf *ofpacts,
> -                     enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +parse_SET_MPLS_LABEL(char *arg, const struct ofpact_parse_params *pp)
>  {
> -    struct ofpact_mpls_label *mpls_label = ofpact_put_SET_MPLS_LABEL(
> ofpacts);
> +    struct ofpact_mpls_label *mpls_label
> +        = ofpact_put_SET_MPLS_LABEL(pp->ofpacts);
>      if (*arg == '\0') {
>          return xstrdup("set_mpls_label: expected label.");
>      }
> @@ -3560,10 +3469,9 @@ parse_SET_MPLS_LABEL(char *arg,
>
>  static void
>  format_SET_MPLS_LABEL(const struct ofpact_mpls_label *a,
> -                      const struct ofputil_port_map *port_map OVS_UNUSED,
> -                      struct ds *s)
> +                      const struct ofpact_format_params *fp)
>  {
> -    ds_put_format(s, "%sset_mpls_label(%s%"PRIu32"%s)%s",
> +    ds_put_format(fp->s, "%sset_mpls_label(%s%"PRIu32"%s)%s",
>                    colors.paren, colors.end, ntohl(a->label),
>                    colors.paren, colors.end);
>  }
> @@ -3591,12 +3499,9 @@ encode_SET_MPLS_TC(const struct ofpact_mpls_tc *tc,
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_SET_MPLS_TC(char *arg,
> -                  const struct ofputil_port_map *port_map OVS_UNUSED,
> -                  struct ofpbuf *ofpacts,
> -                  enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +parse_SET_MPLS_TC(char *arg, const struct ofpact_parse_params *pp)
>  {
> -    struct ofpact_mpls_tc *mpls_tc = ofpact_put_SET_MPLS_TC(ofpacts);
> +    struct ofpact_mpls_tc *mpls_tc = ofpact_put_SET_MPLS_TC(pp->ofpacts);
>
>      if (*arg == '\0') {
>          return xstrdup("set_mpls_tc: expected tc.");
> @@ -3608,10 +3513,9 @@ parse_SET_MPLS_TC(char *arg,
>
>  static void
>  format_SET_MPLS_TC(const struct ofpact_mpls_tc *a,
> -                   const struct ofputil_port_map *port_map OVS_UNUSED,
> -                   struct ds *s)
> +                   const struct ofpact_format_params *fp)
>  {
> -    ds_put_format(s, "%sset_mpls_ttl(%s%"PRIu8"%s)%s",
> +    ds_put_format(fp->s, "%sset_mpls_ttl(%s%"PRIu8"%s)%s",
>                    colors.paren, colors.end, a->tc,
>                    colors.paren, colors.end);
>  }
> @@ -3635,17 +3539,14 @@ encode_SET_MPLS_TTL(const struct ofpact_mpls_ttl
> *ttl,
>  }
>
>  /* Parses 'arg' as the argument to a "set_mpls_ttl" action, and appends
> such an
> - * action to 'ofpacts'.
> + * action to 'pp->ofpacts'.
>   *
>   * Returns NULL if successful, otherwise a malloc()'d string describing
> the
>   * error.  The caller is responsible for freeing the returned string. */
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_SET_MPLS_TTL(char *arg,
> -                   const struct ofputil_port_map *port_map OVS_UNUSED,
> -                   struct ofpbuf *ofpacts,
> -                   enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +parse_SET_MPLS_TTL(char *arg, const struct ofpact_parse_params *pp)
>  {
> -    struct ofpact_mpls_ttl *mpls_ttl = ofpact_put_SET_MPLS_TTL(ofpacts);
> +    struct ofpact_mpls_ttl *mpls_ttl = ofpact_put_SET_MPLS_TTL(pp->
> ofpacts);
>
>      if (*arg == '\0') {
>          return xstrdup("set_mpls_ttl: expected ttl.");
> @@ -3657,10 +3558,9 @@ parse_SET_MPLS_TTL(char *arg,
>
>  static void
>  format_SET_MPLS_TTL(const struct ofpact_mpls_ttl *a,
> -                    const struct ofputil_port_map *port_map OVS_UNUSED,
> -                    struct ds *s)
> +                    const struct ofpact_format_params *fp)
>  {
> -    ds_put_format(s, "%sset_mpls_ttl(%s%"PRIu8"%s)%s",
> +    ds_put_format(fp->s, "%sset_mpls_ttl(%s%"PRIu8"%s)%s",
>                    colors.paren, colors.end, a->ttl,
>                    colors.paren, colors.end);
>  }
> @@ -3682,21 +3582,17 @@ encode_DEC_MPLS_TTL(const struct ofpact_null *null
> OVS_UNUSED,
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_DEC_MPLS_TTL(char *arg OVS_UNUSED,
> -                   const struct ofputil_port_map *port_map OVS_UNUSED,
> -                   struct ofpbuf *ofpacts,
> -                   enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +parse_DEC_MPLS_TTL(char *arg OVS_UNUSED, const struct ofpact_parse_params
> *pp)
>  {
> -    ofpact_put_DEC_MPLS_TTL(ofpacts);
> +    ofpact_put_DEC_MPLS_TTL(pp->ofpacts);
>      return NULL;
>  }
>
>  static void
>  format_DEC_MPLS_TTL(const struct ofpact_null *a OVS_UNUSED,
> -                    const struct ofputil_port_map *port_map OVS_UNUSED,
> -                    struct ds *s)
> +                    const struct ofpact_format_params *fp)
>  {
> -    ds_put_format(s, "%sdec_mpls_ttl%s", colors.value, colors.end);
> +    ds_put_format(fp->s, "%sdec_mpls_ttl%s", colors.value, colors.end);
>  }
>
>  /* Push MPLS label action. */
> @@ -3725,27 +3621,23 @@ encode_PUSH_MPLS(const struct ofpact_push_mpls
> *push_mpls,
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_PUSH_MPLS(char *arg,
> -                const struct ofputil_port_map *port_map OVS_UNUSED,
> -                struct ofpbuf *ofpacts,
> -                enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +parse_PUSH_MPLS(char *arg, const struct ofpact_parse_params *pp)
>  {
>      uint16_t ethertype;
>      char *error;
>
>      error = str_to_u16(arg, "push_mpls", &ethertype);
>      if (!error) {
> -        ofpact_put_PUSH_MPLS(ofpacts)->ethertype = htons(ethertype);
> +        ofpact_put_PUSH_MPLS(pp->ofpacts)->ethertype = htons(ethertype);
>      }
>      return error;
>  }
>
>  static void
>  format_PUSH_MPLS(const struct ofpact_push_mpls *a,
> -                 const struct ofputil_port_map *port_map OVS_UNUSED,
> -                 struct ds *s)
> +                 const struct ofpact_format_params *fp)
>  {
> -    ds_put_format(s, "%spush_mpls:%s0x%04"PRIx16,
> +    ds_put_format(fp->s, "%spush_mpls:%s0x%04"PRIx16,
>                    colors.param, colors.end, ntohs(a->ethertype));
>  }
>
> @@ -3768,27 +3660,23 @@ encode_POP_MPLS(const struct ofpact_pop_mpls
> *pop_mpls,
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_POP_MPLS(char *arg,
> -               const struct ofputil_port_map *port_map OVS_UNUSED,
> -               struct ofpbuf *ofpacts,
> -               enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +parse_POP_MPLS(char *arg, const struct ofpact_parse_params *pp)
>  {
>      uint16_t ethertype;
>      char *error;
>
>      error = str_to_u16(arg, "pop_mpls", &ethertype);
>      if (!error) {
> -        ofpact_put_POP_MPLS(ofpacts)->ethertype = htons(ethertype);
> +        ofpact_put_POP_MPLS(pp->ofpacts)->ethertype = htons(ethertype);
>      }
>      return error;
>  }
>
>  static void
>  format_POP_MPLS(const struct ofpact_pop_mpls *a,
> -                const struct ofputil_port_map *port_map OVS_UNUSED,
> -                struct ds *s)
> +                const struct ofpact_format_params *fp)
>  {
> -    ds_put_format(s, "%spop_mpls:%s0x%04"PRIx16,
> +    ds_put_format(fp->s, "%spop_mpls:%s0x%04"PRIx16,
>                    colors.param, colors.end, ntohs(a->ethertype));
>  }
>
> @@ -3835,31 +3723,27 @@ encode_SET_TUNNEL(const struct ofpact_tunnel
> *tunnel,
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_set_tunnel(char *arg, struct ofpbuf *ofpacts,
> -                 enum ofp_raw_action_type raw)
> +parse_set_tunnel(char *arg, enum ofp_raw_action_type raw,
> +                 const struct ofpact_parse_params *pp)
>  {
>      struct ofpact_tunnel *tunnel;
>
> -    tunnel = ofpact_put_SET_TUNNEL(ofpacts);
> +    tunnel = ofpact_put_SET_TUNNEL(pp->ofpacts);
>      tunnel->ofpact.raw = raw;
>      return str_to_u64(arg, &tunnel->tun_id);
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_SET_TUNNEL(char *arg,
> -                 const struct ofputil_port_map *port_map OVS_UNUSED,
> -                 struct ofpbuf *ofpacts,
> -                 enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +parse_SET_TUNNEL(char *arg, const struct ofpact_parse_params *pp)
>  {
> -    return parse_set_tunnel(arg, ofpacts, NXAST_RAW_SET_TUNNEL);
> +    return parse_set_tunnel(arg, NXAST_RAW_SET_TUNNEL, pp);
>  }
>
>  static void
>  format_SET_TUNNEL(const struct ofpact_tunnel *a,
> -                  const struct ofputil_port_map *port_map OVS_UNUSED,
> -                  struct ds *s)
> +                  const struct ofpact_format_params *fp)
>  {
> -    ds_put_format(s, "%sset_tunnel%s:%s%#"PRIx64, colors.param,
> +    ds_put_format(fp->s, "%sset_tunnel%s:%s%#"PRIx64, colors.param,
>                    (a->tun_id > UINT32_MAX
>                     || a->ofpact.raw == NXAST_RAW_SET_TUNNEL64 ? "64" :
> ""),
>                    colors.end, a->tun_id);
> @@ -3884,20 +3768,16 @@ encode_SET_QUEUE(const struct ofpact_queue *queue,
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_SET_QUEUE(char *arg,
> -                const struct ofputil_port_map *port_map OVS_UNUSED,
> -                struct ofpbuf *ofpacts,
> -                enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +parse_SET_QUEUE(char *arg, const struct ofpact_parse_params *pp)
>  {
> -    return str_to_u32(arg, &ofpact_put_SET_QUEUE(ofpacts)->queue_id);
> +    return str_to_u32(arg, &ofpact_put_SET_QUEUE(pp->ofpacts)->queue_id);
>  }
>
>  static void
>  format_SET_QUEUE(const struct ofpact_queue *a,
> -                 const struct ofputil_port_map *port_map OVS_UNUSED,
> -                 struct ds *s)
> +                 const struct ofpact_format_params *fp)
>  {
> -    ds_put_format(s, "%sset_queue:%s%"PRIu32,
> +    ds_put_format(fp->s, "%sset_queue:%s%"PRIu32,
>                    colors.param, colors.end, a->queue_id);
>  }
>
> @@ -3919,20 +3799,17 @@ encode_POP_QUEUE(const struct ofpact_null *null
> OVS_UNUSED,
>
>  static char * OVS_WARN_UNUSED_RESULT
>  parse_POP_QUEUE(const char *arg OVS_UNUSED,
> -                const struct ofputil_port_map *port_map OVS_UNUSED,
> -                struct ofpbuf *ofpacts,
> -                enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +                const struct ofpact_parse_params *pp)
>  {
> -    ofpact_put_POP_QUEUE(ofpacts);
> +    ofpact_put_POP_QUEUE(pp->ofpacts);
>      return NULL;
>  }
>
>  static void
>  format_POP_QUEUE(const struct ofpact_null *a OVS_UNUSED,
> -                 const struct ofputil_port_map *port_map OVS_UNUSED,
> -                 struct ds *s)
> +                 const struct ofpact_format_params *fp)
>  {
> -    ds_put_format(s, "%spop_queue%s", colors.value, colors.end);
> +    ds_put_format(fp->s, "%spop_queue%s", colors.value, colors.end);
>  }
>
>  /* Action structure for NXAST_FIN_TIMEOUT.
> @@ -3993,12 +3870,9 @@ encode_FIN_TIMEOUT(const struct ofpact_fin_timeout
> *fin_timeout,
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_FIN_TIMEOUT(char *arg,
> -                  const struct ofputil_port_map *port_map OVS_UNUSED,
> -                  struct ofpbuf *ofpacts,
> -                  enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +parse_FIN_TIMEOUT(char *arg, const struct ofpact_parse_params *pp)
>  {
> -    struct ofpact_fin_timeout *oft = ofpact_put_FIN_TIMEOUT(ofpacts);
> +    struct ofpact_fin_timeout *oft = ofpact_put_FIN_TIMEOUT(pp->ofpacts);
>      char *key, *value;
>
>      while (ofputil_parse_key_value(&arg, &key, &value)) {
> @@ -4022,20 +3896,19 @@ parse_FIN_TIMEOUT(char *arg,
>
>  static void
>  format_FIN_TIMEOUT(const struct ofpact_fin_timeout *a,
> -                   const struct ofputil_port_map *port_map OVS_UNUSED,
> -                   struct ds *s)
> +                   const struct ofpact_format_params *fp)
>  {
> -    ds_put_format(s, "%sfin_timeout(%s", colors.paren, colors.end);
> +    ds_put_format(fp->s, "%sfin_timeout(%s", colors.paren, colors.end);
>      if (a->fin_idle_timeout) {
> -        ds_put_format(s, "%sidle_timeout=%s%"PRIu16",",
> +        ds_put_format(fp->s, "%sidle_timeout=%s%"PRIu16",",
>                        colors.param, colors.end, a->fin_idle_timeout);
>      }
>      if (a->fin_hard_timeout) {
> -        ds_put_format(s, "%shard_timeout=%s%"PRIu16",",
> +        ds_put_format(fp->s, "%shard_timeout=%s%"PRIu16",",
>                        colors.param, colors.end, a->fin_hard_timeout);
>      }
> -    ds_chomp(s, ',');
> -    ds_put_format(s, "%s)%s", colors.paren, colors.end);
> +    ds_chomp(fp->s, ',');
> +    ds_put_format(fp->s, "%s)%s", colors.paren, colors.end);
>  }
>
>  /* Action structure for NXAST_ENCAP */
> @@ -4149,10 +4022,7 @@ parse_ed_props(const uint16_t prop_class, char
> **arg, int *n_props, struct ofpbu
>   */
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_ENCAP(char *arg,
> -            const struct ofputil_port_map *port_map OVS_UNUSED,
> -            struct ofpbuf *out,
> -            enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +parse_ENCAP(char *arg, const struct ofpact_parse_params *pp)
>  {
>      struct ofpact_encap *encap;
>      char *key, *value, *str;
> @@ -4160,7 +4030,7 @@ parse_ENCAP(char *arg,
>      uint16_t prop_class;
>      int n_props = 0;
>
> -    encap = ofpact_put_ENCAP(out);
> +    encap = ofpact_put_ENCAP(pp->ofpacts);
>      encap->hdr_size = 0;
>      /* Parse encap header type. */
>      str = arg;
> @@ -4174,14 +4044,14 @@ parse_ENCAP(char *arg,
>          return xasprintf("Invalid encap prop class: %s", key);
>      }
>      /* Parse encap properties. */
> -    error = parse_ed_props(prop_class, &value, &n_props, out);
> +    error = parse_ed_props(prop_class, &value, &n_props, pp->ofpacts);
>      if (error != NULL) {
>          return error;
>      }
> -    /* ofbuf out may have been re-allocated. */
> -    encap = out->header;
> +    /* ofpbuf may have been re-allocated. */
> +    encap = pp->ofpacts->header;
>      encap->n_props = n_props;
> -    ofpact_finish_ENCAP(out, &encap);
> +    ofpact_finish_ENCAP(pp->ofpacts, &encap);
>      return NULL;
>  }
>
> @@ -4221,17 +4091,16 @@ format_ed_props(struct ds *s, uint16_t n_props,
>
>  static void
>  format_ENCAP(const struct ofpact_encap *a,
> -             const struct ofputil_port_map *port_map OVS_UNUSED,
> -             struct ds *s)
> +             const struct ofpact_format_params *fp)
>  {
> -    ds_put_format(s, "%sencap(%s", colors.paren, colors.end);
> -    ds_put_format(s, "%s", format_encap_pkt_type(a->new_pkt_type));
> +    ds_put_format(fp->s, "%sencap(%s", colors.paren, colors.end);
> +    ds_put_format(fp->s, "%s", format_encap_pkt_type(a->new_pkt_type));
>      if (a->n_props > 0) {
> -        ds_put_format(s, "%s(%s", colors.paren, colors.end);
> -        format_ed_props(s, a->n_props, a->props);
> -        ds_put_format(s, "%s)%s", colors.paren, colors.end);
> +        ds_put_format(fp->s, "%s(%s", colors.paren, colors.end);
> +        format_ed_props(fp->s, a->n_props, a->props);
> +        ds_put_format(fp->s, "%s)%s", colors.paren, colors.end);
>      }
> -    ds_put_format(s, "%s)%s", colors.paren, colors.end);
> +    ds_put_format(fp->s, "%s)%s", colors.paren, colors.end);
>  }
>
>  /* Action structure for NXAST_DECAP */
> @@ -4281,17 +4150,14 @@ encode_DECAP(const struct ofpact_decap *decap,
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_DECAP(char *arg,
> -            const struct ofputil_port_map *port_map OVS_UNUSED,
> -            struct ofpbuf *ofpacts,
> -            enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +parse_DECAP(char *arg, const struct ofpact_parse_params *pp)
>  {
>      struct ofpact_decap *decap;
>      char *key, *value, *pos;
>      char *error = NULL;
>      uint16_t ns, type;
>
> -    decap = ofpact_put_DECAP(ofpacts);
> +    decap = ofpact_put_DECAP(pp->ofpacts);
>      /* Default next packet_type is PT_USE_NEXT_PROTO. */
>      decap->new_pkt_type = htonl(PT_USE_NEXT_PROTO);
>
> @@ -4328,16 +4194,15 @@ parse_DECAP(char *arg,
>
>  static void
>  format_DECAP(const struct ofpact_decap *a,
> -             const struct ofputil_port_map *port_map OVS_UNUSED,
> -             struct ds *s)
> +             const struct ofpact_format_params *fp)
>  {
> -    ds_put_format(s, "%sdecap(%s", colors.paren, colors.end);
> +    ds_put_format(fp->s, "%sdecap(%s", colors.paren, colors.end);
>      if (a->new_pkt_type != htonl(PT_USE_NEXT_PROTO)) {
> -        ds_put_format(s, "packet_type(ns=%"PRIu16",id=%#"PRIx16")",
> +        ds_put_format(fp->s, "packet_type(ns=%"PRIu16",id=%#"PRIx16")",
>                        pt_ns(a->new_pkt_type),
>                        pt_ns_type(a->new_pkt_type));
>      }
> -    ds_put_format(s, "%s)%s", colors.paren, colors.end);
> +    ds_put_format(fp->s, "%s)%s", colors.paren, colors.end);
>  }
>
>  /* Action dec_nsh_ttl */
> @@ -4357,20 +4222,17 @@ encode_DEC_NSH_TTL(const struct ofpact_null *null
> OVS_UNUSED,
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_DEC_NSH_TTL(char *arg OVS_UNUSED,
> -           const struct ofputil_port_map *port_map OVS_UNUSED,
> -           struct ofpbuf *ofpacts,
> -           enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +parse_DEC_NSH_TTL(char *arg OVS_UNUSED, const struct ofpact_parse_params
> *pp)
>  {
> -    ofpact_put_DEC_NSH_TTL(ofpacts);
> +    ofpact_put_DEC_NSH_TTL(pp->ofpacts);
>      return NULL;
>  }
>
>  static void
>  format_DEC_NSH_TTL(const struct ofpact_null *a OVS_UNUSED,
> -            const struct ofputil_port_map *port_map OVS_UNUSED, struct ds
> *s)
> +                   const struct ofpact_format_params *fp)
>  {
> -    ds_put_format(s, "%sdec_nsh_ttl%s", colors.special, colors.end);
> +    ds_put_format(fp->s, "%sdec_nsh_ttl%s", colors.special, colors.end);
>  }
>
>
> @@ -4517,19 +4379,16 @@ encode_RESUBMIT(const struct ofpact_resubmit
> *resubmit,
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_RESUBMIT(char *arg,
> -               const struct ofputil_port_map *port_map,
> -               struct ofpbuf *ofpacts,
> -               enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +parse_RESUBMIT(char *arg, const struct ofpact_parse_params *pp)
>  {
>      struct ofpact_resubmit *resubmit;
>      char *in_port_s, *table_s, *ct_s;
>
> -    resubmit = ofpact_put_RESUBMIT(ofpacts);
> +    resubmit = ofpact_put_RESUBMIT(pp->ofpacts);
>
>      in_port_s = strsep(&arg, ",");
>      if (in_port_s && in_port_s[0]) {
> -        if (!ofputil_port_from_string(in_port_s, port_map,
> +        if (!ofputil_port_from_string(in_port_s, pp->port_map,
>                                        &resubmit->in_port)) {
>              return xasprintf("%s: resubmit to unknown port", in_port_s);
>          }
> @@ -4570,24 +4429,24 @@ parse_RESUBMIT(char *arg,
>
>  static void
>  format_RESUBMIT(const struct ofpact_resubmit *a,
> -                const struct ofputil_port_map *port_map, struct ds *s)
> +                const struct ofpact_format_params *fp)
>  {
>      if (a->in_port != OFPP_IN_PORT && a->table_id == 255) {
> -        ds_put_format(s, "%sresubmit:%s", colors.special, colors.end);
> -        ofputil_format_port(a->in_port, port_map, s);
> +        ds_put_format(fp->s, "%sresubmit:%s", colors.special, colors.end);
> +        ofputil_format_port(a->in_port, fp->port_map, fp->s);
>      } else {
> -        ds_put_format(s, "%sresubmit(%s", colors.paren, colors.end);
> +        ds_put_format(fp->s, "%sresubmit(%s", colors.paren, colors.end);
>          if (a->in_port != OFPP_IN_PORT) {
> -            ofputil_format_port(a->in_port, port_map, s);
> +            ofputil_format_port(a->in_port, fp->port_map, fp->s);
>          }
> -        ds_put_char(s, ',');
> +        ds_put_char(fp->s, ',');
>          if (a->table_id != 255) {
> -            ds_put_format(s, "%"PRIu8, a->table_id);
> +            ds_put_format(fp->s, "%"PRIu8, a->table_id);
>          }
>          if (a->with_ct_orig) {
> -            ds_put_cstr(s, ",ct");
> +            ds_put_cstr(fp->s, ",ct");
>          }
> -        ds_put_format(s, "%s)%s", colors.paren, colors.end);
> +        ds_put_format(fp->s, "%s)%s", colors.paren, colors.end);
>      }
>  }
>
> @@ -5163,18 +5022,16 @@ encode_LEARN(const struct ofpact_learn *learn,
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_LEARN(char *arg, const struct ofputil_port_map *port_map,
> -            struct ofpbuf *ofpacts,
> -            enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +parse_LEARN(char *arg, const struct ofpact_parse_params *pp)
>  {
> -    return learn_parse(arg, port_map, ofpacts);
> +    return learn_parse(arg, pp->port_map, pp->ofpacts);
>  }
>
>  static void
>  format_LEARN(const struct ofpact_learn *a,
> -             const struct ofputil_port_map *port_map, struct ds *s)
> +             const struct ofpact_format_params *fp)
>  {
> -    learn_format(a, port_map, s);
> +    learn_format(a, fp->port_map, fp->s);
>  }
>
>  /* Action structure for NXAST_CONJUNCTION. */
> @@ -5227,20 +5084,16 @@ encode_CONJUNCTION(const struct ofpact_conjunction
> *oc,
>
>  static void
>  format_CONJUNCTION(const struct ofpact_conjunction *oc,
> -                   const struct ofputil_port_map *port_map OVS_UNUSED,
> -                   struct ds *s)
> +                   const struct ofpact_format_params *fp)
>  {
> -    ds_put_format(s, "%sconjunction(%s%"PRIu32",%d/%"PRIu8"%s)%s",
> +    ds_put_format(fp->s, "%sconjunction(%s%"PRIu32",%d/%"PRIu8"%s)%s",
>                    colors.paren, colors.end,
>                    oc->id, oc->clause + 1, oc->n_clauses,
>                    colors.paren, colors.end);
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_CONJUNCTION(const char *arg,
> -                  const struct ofputil_port_map *port_map OVS_UNUSED,
> -                  struct ofpbuf *ofpacts,
> -                  enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +parse_CONJUNCTION(const char *arg, const struct ofpact_parse_params *pp)
>  {
>      uint8_t n_clauses;
>      uint8_t clause;
> @@ -5263,7 +5116,7 @@ parse_CONJUNCTION(const char *arg,
>                         "number of clauses");
>      }
>
> -    add_conjunction(ofpacts, id, clause - 1, n_clauses);
> +    add_conjunction(pp->ofpacts, id, clause - 1, n_clauses);
>      return NULL;
>  }
>
> @@ -5382,20 +5235,16 @@ encode_MULTIPATH(const struct ofpact_multipath *mp,
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_MULTIPATH(const char *arg,
> -                const struct ofputil_port_map *port_map OVS_UNUSED,
> -                struct ofpbuf *ofpacts,
> -                enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +parse_MULTIPATH(const char *arg, const struct ofpact_parse_params *pp)
>  {
> -    return multipath_parse(ofpact_put_MULTIPATH(ofpacts), arg);
> +    return multipath_parse(ofpact_put_MULTIPATH(pp->ofpacts), arg);
>  }
>
>  static void
>  format_MULTIPATH(const struct ofpact_multipath *a,
> -                 const struct ofputil_port_map *port_map OVS_UNUSED,
> -                 struct ds *s)
> +                 const struct ofpact_format_params *fp)
>  {
> -    multipath_format(a, s);
> +    multipath_format(a, fp->s);
>  }
>
>  /* Action structure for NXAST_NOTE.
> @@ -5449,30 +5298,27 @@ encode_NOTE(const struct ofpact_note *note,
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_NOTE(const char *arg,
> -           const struct ofputil_port_map *port_map OVS_UNUSED,
> -           struct ofpbuf *ofpacts,
> -           enum ofputil_protocol *usable_protocols OVS_UNUSED)
> -{
> -    size_t start_ofs = ofpacts->size;
> -    ofpact_put_NOTE(ofpacts);
> -    arg = ofpbuf_put_hex(ofpacts, arg, NULL);
> +parse_NOTE(const char *arg, const struct ofpact_parse_params *pp)
> +{
> +    size_t start_ofs = pp->ofpacts->size;
> +    ofpact_put_NOTE(pp->ofpacts);
> +    arg = ofpbuf_put_hex(pp->ofpacts, arg, NULL);
>      if (arg[0]) {
>          return xstrdup("bad hex digit in `note' argument");
>      }
> -    struct ofpact_note *note = ofpbuf_at_assert(ofpacts, start_ofs,
> +    struct ofpact_note *note = ofpbuf_at_assert(pp->ofpacts, start_ofs,
>                                                  sizeof *note);
> -    note->length = ofpacts->size - (start_ofs + sizeof *note);
> -    ofpact_finish_NOTE(ofpacts, &note);
> +    note->length = pp->ofpacts->size - (start_ofs + sizeof *note);
> +    ofpact_finish_NOTE(pp->ofpacts, &note);
>      return NULL;
>  }
>
>  static void
>  format_NOTE(const struct ofpact_note *a,
> -            const struct ofputil_port_map *port_map OVS_UNUSED, struct ds
> *s)
> +            const struct ofpact_format_params *fp)
>  {
> -    ds_put_format(s, "%snote:%s", colors.param, colors.end);
> -    format_hex_arg(s, a->data, a->length);
> +    ds_put_format(fp->s, "%snote:%s", colors.param, colors.end);
> +    format_hex_arg(fp->s, a->data, a->length);
>  }
>
>  /* Exit action. */
> @@ -5492,20 +5338,17 @@ encode_EXIT(const struct ofpact_null *null
> OVS_UNUSED,
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_EXIT(char *arg OVS_UNUSED,
> -           const struct ofputil_port_map *port_map OVS_UNUSED,
> -           struct ofpbuf *ofpacts,
> -           enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +parse_EXIT(char *arg OVS_UNUSED, const struct ofpact_parse_params *pp)
>  {
> -    ofpact_put_EXIT(ofpacts);
> +    ofpact_put_EXIT(pp->ofpacts);
>      return NULL;
>  }
>
>  static void
>  format_EXIT(const struct ofpact_null *a OVS_UNUSED,
> -            const struct ofputil_port_map *port_map OVS_UNUSED, struct ds
> *s)
> +            const struct ofpact_format_params *fp)
>  {
> -    ds_put_format(s, "%sexit%s", colors.special, colors.end);
> +    ds_put_format(fp->s, "%sexit%s", colors.special, colors.end);
>  }
>
>  /* Unroll xlate action. */
> @@ -5520,19 +5363,16 @@ encode_UNROLL_XLATE(const struct
> ofpact_unroll_xlate *unroll OVS_UNUSED,
>
>  static char * OVS_WARN_UNUSED_RESULT
>  parse_UNROLL_XLATE(char *arg OVS_UNUSED,
> -                   const struct ofputil_port_map *port_map OVS_UNUSED,
> -                   struct ofpbuf *ofpacts OVS_UNUSED,
> -                   enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +                   const struct ofpact_parse_params *pp OVS_UNUSED)
>  {
>      OVS_NOT_REACHED();
>  }
>
>  static void
>  format_UNROLL_XLATE(const struct ofpact_unroll_xlate *a,
> -                    const struct ofputil_port_map *port_map OVS_UNUSED,
> -                    struct ds *s)
> +                    const struct ofpact_format_params *fp)
>  {
> -    ds_put_format(s, "%sunroll_xlate(%s%stable=%s%"PRIu8
> +    ds_put_format(fp->s, "%sunroll_xlate(%s%stable=%s%"PRIu8
>                    ", %scookie=%s%"PRIu64"%s)%s",
>                    colors.paren,   colors.end,
>                    colors.special, colors.end, a->rule_table_id,
> @@ -5587,33 +5427,30 @@ encode_CLONE(const struct ofpact_nest *clone,
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_CLONE(char *arg, const struct ofputil_port_map *port_map,
> -            struct ofpbuf *ofpacts,
> -            enum ofputil_protocol *usable_protocols)
> +parse_CLONE(char *arg, const struct ofpact_parse_params *pp)
>  {
> -    const size_t clone_offset = ofpacts_pull(ofpacts);
> -    struct ofpact_nest *clone = ofpact_put_CLONE(ofpacts);
> +    const size_t clone_offset = ofpacts_pull(pp->ofpacts);
> +    struct ofpact_nest *clone = ofpact_put_CLONE(pp->ofpacts);
>      char *error;
>
> -    ofpbuf_pull(ofpacts, sizeof *clone);
> -    error = ofpacts_parse_copy(arg, port_map, ofpacts,
> -                               usable_protocols, false, 0);
> +    ofpbuf_pull(pp->ofpacts, sizeof *clone);
> +    error = ofpacts_parse_copy(arg, pp, false, 0);
>      /* header points to the action list */
> -    ofpacts->header = ofpbuf_push_uninit(ofpacts, sizeof *clone);
> -    clone = ofpacts->header;
> +    pp->ofpacts->header = ofpbuf_push_uninit(pp->ofpacts, sizeof *clone);
> +    clone = pp->ofpacts->header;
>
> -    ofpact_finish_CLONE(ofpacts, &clone);
> -    ofpbuf_push_uninit(ofpacts, clone_offset);
> +    ofpact_finish_CLONE(pp->ofpacts, &clone);
> +    ofpbuf_push_uninit(pp->ofpacts, clone_offset);
>      return error;
>  }
>
>  static void
>  format_CLONE(const struct ofpact_nest *a,
> -             const struct ofputil_port_map *port_map, struct ds *s)
> +             const struct ofpact_format_params *fp)
>  {
> -    ds_put_format(s, "%sclone(%s", colors.paren, colors.end);
> -    ofpacts_format(a->actions, ofpact_nest_get_action_len(a), port_map,
> s);
> -    ds_put_format(s, "%s)%s", colors.paren, colors.end);
> +    ds_put_format(fp->s, "%sclone(%s", colors.paren, colors.end);
> +    ofpacts_format(a->actions, ofpact_nest_get_action_len(a), fp);
> +    ds_put_format(fp->s, "%s)%s", colors.paren, colors.end);
>  }
>
>  /* Action structure for NXAST_SAMPLE.
> @@ -5766,16 +5603,14 @@ encode_SAMPLE(const struct ofpact_sample *sample,
>  }
>
>  /* Parses 'arg' as the argument to a "sample" action, and appends such an
> - * action to 'ofpacts'.
> + * action to 'pp->ofpacts'.
>   *
>   * Returns NULL if successful, otherwise a malloc()'d string describing
> the
>   * error.  The caller is responsible for freeing the returned string. */
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_SAMPLE(char *arg, const struct ofputil_port_map *port_map,
> -             struct ofpbuf *ofpacts,
> -             enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +parse_SAMPLE(char *arg, const struct ofpact_parse_params *pp)
>  {
> -    struct ofpact_sample *os = ofpact_put_SAMPLE(ofpacts);
> +    struct ofpact_sample *os = ofpact_put_SAMPLE(pp->ofpacts);
>      os->sampling_port = OFPP_NONE;
>      os->direction = NX_ACTION_SAMPLE_DEFAULT;
>
> @@ -5795,7 +5630,7 @@ parse_SAMPLE(char *arg, const struct
> ofputil_port_map *port_map,
>          } else if (!strcmp(key, "obs_point_id")) {
>              error = str_to_u32(value, &os->obs_point_id);
>          } else if (!strcmp(key, "sampling_port")) {
> -            if (!ofputil_port_from_string(value, port_map,
> +            if (!ofputil_port_from_string(value, pp->port_map,
>                                            &os->sampling_port)) {
>                  error = xasprintf("%s: unknown port", value);
>              }
> @@ -5820,9 +5655,9 @@ parse_SAMPLE(char *arg, const struct
> ofputil_port_map *port_map,
>
>  static void
>  format_SAMPLE(const struct ofpact_sample *a,
> -              const struct ofputil_port_map *port_map, struct ds *s)
> +              const struct ofpact_format_params *fp)
>  {
> -    ds_put_format(s, "%ssample(%s%sprobability=%s%"PRIu16
> +    ds_put_format(fp->s, "%ssample(%s%sprobability=%s%"PRIu16
>                    ",%scollector_set_id=%s%"PRIu32
>                    ",%sobs_domain_id=%s%"PRIu32
>                    ",%sobs_point_id=%s%"PRIu32,
> @@ -5832,15 +5667,15 @@ format_SAMPLE(const struct ofpact_sample *a,
>                    colors.param, colors.end, a->obs_domain_id,
>                    colors.param, colors.end, a->obs_point_id);
>      if (a->sampling_port != OFPP_NONE) {
> -        ds_put_format(s, ",%ssampling_port=%s", colors.param, colors.end);
> -        ofputil_format_port(a->sampling_port, port_map, s);
> +        ds_put_format(fp->s, ",%ssampling_port=%s", colors.param,
> colors.end);
> +        ofputil_format_port(a->sampling_port, fp->port_map, fp->s);
>      }
>      if (a->direction == NX_ACTION_SAMPLE_INGRESS) {
> -        ds_put_format(s, ",%singress%s", colors.param, colors.end);
> +        ds_put_format(fp->s, ",%singress%s", colors.param, colors.end);
>      } else if (a->direction == NX_ACTION_SAMPLE_EGRESS) {
> -        ds_put_format(s, ",%segress%s", colors.param, colors.end);
> +        ds_put_format(fp->s, ",%segress%s", colors.param, colors.end);
>      }
> -    ds_put_format(s, "%s)%s", colors.paren, colors.end);
> +    ds_put_format(fp->s, "%s)%s", colors.paren, colors.end);
>  }
>
>  /* debug instructions. */
> @@ -5873,21 +5708,17 @@ encode_DEBUG_RECIRC(const struct ofpact_null *n
> OVS_UNUSED,
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_DEBUG_RECIRC(char *arg OVS_UNUSED,
> -                   const struct ofputil_port_map *port_map OVS_UNUSED,
> -                   struct ofpbuf *ofpacts,
> -                   enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +parse_DEBUG_RECIRC(char *arg OVS_UNUSED, const struct ofpact_parse_params
> *pp)
>  {
> -    ofpact_put_DEBUG_RECIRC(ofpacts);
> +    ofpact_put_DEBUG_RECIRC(pp->ofpacts);
>      return NULL;
>  }
>
>  static void
>  format_DEBUG_RECIRC(const struct ofpact_null *a OVS_UNUSED,
> -                    const struct ofputil_port_map *port_map OVS_UNUSED,
> -                    struct ds *s)
> +                    const struct ofpact_format_params *fp)
>  {
> -    ds_put_format(s, "%sdebug_recirc%s", colors.value, colors.end);
> +    ds_put_format(fp->s, "%sdebug_recirc%s", colors.value, colors.end);
>  }
>
>  static enum ofperr
> @@ -5910,21 +5741,17 @@ encode_DEBUG_SLOW(const struct ofpact_null *n
> OVS_UNUSED,
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_DEBUG_SLOW(char *arg OVS_UNUSED,
> -                 const struct ofputil_port_map *port_map OVS_UNUSED,
> -                 struct ofpbuf *ofpacts,
> -                 enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +parse_DEBUG_SLOW(char *arg OVS_UNUSED, const struct ofpact_parse_params
> *pp)
>  {
> -    ofpact_put_DEBUG_SLOW(ofpacts);
> +    ofpact_put_DEBUG_SLOW(pp->ofpacts);
>      return NULL;
>  }
>
>  static void
>  format_DEBUG_SLOW(const struct ofpact_null *a OVS_UNUSED,
> -                  const struct ofputil_port_map *port_map OVS_UNUSED,
> -                  struct ds *s)
> +                  const struct ofpact_format_params *fp)
>  {
> -    ds_put_format(s, "%sdebug_slow%s", colors.value, colors.end);
> +    ds_put_format(fp->s, "%sdebug_slow%s", colors.value, colors.end);
>  }
>
>  /* Action structure for NXAST_CT.
> @@ -6187,25 +6014,22 @@ encode_CT(const struct ofpact_conntrack *conntrack,
>  }
>
>  static char *OVS_WARN_UNUSED_RESULT
> -parse_NAT(char *arg, const struct ofputil_port_map *port_map OVS_UNUSED,
> -          struct ofpbuf *, enum ofputil_protocol * OVS_UNUSED);
> +parse_NAT(char *arg, const struct ofpact_parse_params *pp);
>
>  /* Parses 'arg' as the argument to a "ct" action, and appends such an
> - * action to 'ofpacts'.
> + * action to 'pp->ofpacts'.
>   *
>   * Returns NULL if successful, otherwise a malloc()'d string describing
> the
>   * error.  The caller is responsible for freeing the returned string. */
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_CT(char *arg, const struct ofputil_port_map *port_map,
> -         struct ofpbuf *ofpacts,
> -         enum ofputil_protocol *usable_protocols)
> +parse_CT(char *arg, const struct ofpact_parse_params *pp)
>  {
> -    const size_t ct_offset = ofpacts_pull(ofpacts);
> +    const size_t ct_offset = ofpacts_pull(pp->ofpacts);
>      struct ofpact_conntrack *oc;
>      char *error = NULL;
>      char *key, *value;
>
> -    oc = ofpact_put_CT(ofpacts);
> +    oc = ofpact_put_CT(pp->ofpacts);
>      oc->flags = 0;
>      oc->recirc_table = NX_CT_RECIRC_NONE;
>      while (ofputil_parse_key_value(&arg, &key, &value)) {
> @@ -6231,26 +6055,27 @@ parse_CT(char *arg, const struct ofputil_port_map
> *port_map,
>          } else if (!strcmp(key, "alg")) {
>              error = str_to_connhelper(value, &oc->alg);
>          } else if (!strcmp(key, "nat")) {
> -            const size_t nat_offset = ofpacts_pull(ofpacts);
> +            const size_t nat_offset = ofpacts_pull(pp->ofpacts);
>
> -            error = parse_NAT(value, port_map, ofpacts, usable_protocols);
> +            error = parse_NAT(value, pp);
>              /* Update CT action pointer and length. */
> -            ofpacts->header = ofpbuf_push_uninit(ofpacts, nat_offset);
> -            oc = ofpacts->header;
> +            pp->ofpacts->header = ofpbuf_push_uninit(pp->ofpacts,
> nat_offset);
> +            oc = pp->ofpacts->header;
>          } else if (!strcmp(key, "exec")) {
>              /* Hide existing actions from ofpacts_parse_copy(), so the
>               * nesting can be handled transparently. */
>              enum ofputil_protocol usable_protocols2;
> -            const size_t exec_offset = ofpacts_pull(ofpacts);
> +            const size_t exec_offset = ofpacts_pull(pp->ofpacts);
>
>              /* Initializes 'usable_protocol2', fold it back to
>               * '*usable_protocols' afterwards, so that we do not lose
>               * restrictions already in there. */
> -            error = ofpacts_parse_copy(value, port_map, ofpacts,
> -                                       &usable_protocols2, false,
> OFPACT_CT);
> -            *usable_protocols &= usable_protocols2;
> -            ofpacts->header = ofpbuf_push_uninit(ofpacts, exec_offset);
> -            oc = ofpacts->header;
> +            struct ofpact_parse_params pp2 = *pp;
> +            pp2.usable_protocols = &usable_protocols2;
> +            error = ofpacts_parse_copy(value, &pp2, false, OFPACT_CT);
> +            *pp->usable_protocols &= usable_protocols2;
> +            pp->ofpacts->header = ofpbuf_push_uninit(pp->ofpacts,
> exec_offset);
> +            oc = pp->ofpacts->header;
>          } else {
>              error = xasprintf("invalid argument to \"ct\" action: `%s'",
> key);
>          }
> @@ -6261,8 +6086,8 @@ parse_CT(char *arg, const struct ofputil_port_map
> *port_map,
>      if (!error && oc->flags & NX_CT_F_FORCE && !(oc->flags &
> NX_CT_F_COMMIT)) {
>          error = xasprintf("\"force\" flag requires \"commit\" flag.");
>      }
> -    ofpact_finish_CT(ofpacts, &oc);
> -    ofpbuf_push_uninit(ofpacts, ct_offset);
> +    ofpact_finish_CT(pp->ofpacts, &oc);
> +    ofpbuf_push_uninit(pp->ofpacts, ct_offset);
>      return error;
>  }
>
> @@ -6286,31 +6111,29 @@ format_alg(int port, struct ds *s)
>  }
>
>  static void format_NAT(const struct ofpact_nat *,
> -                       const struct ofputil_port_map *port_map,
> -                       struct ds *ds);
> +                       const struct ofpact_format_params *fp);
>
>  static void
>  format_CT(const struct ofpact_conntrack *a,
> -          const struct ofputil_port_map *port_map,
> -          struct ds *s)
> +          const struct ofpact_format_params *fp)
>  {
> -    ds_put_format(s, "%sct(%s", colors.paren, colors.end);
> +    ds_put_format(fp->s, "%sct(%s", colors.paren, colors.end);
>      if (a->flags & NX_CT_F_COMMIT) {
> -        ds_put_format(s, "%scommit%s,", colors.value, colors.end);
> +        ds_put_format(fp->s, "%scommit%s,", colors.value, colors.end);
>      }
>      if (a->flags & NX_CT_F_FORCE) {
> -        ds_put_format(s, "%sforce%s,", colors.value, colors.end);
> +        ds_put_format(fp->s, "%sforce%s,", colors.value, colors.end);
>      }
>      if (a->recirc_table != NX_CT_RECIRC_NONE) {
> -        ds_put_format(s, "%stable=%s%"PRIu8",",
> +        ds_put_format(fp->s, "%stable=%s%"PRIu8",",
>                        colors.special, colors.end, a->recirc_table);
>      }
>      if (a->zone_src.field) {
> -        ds_put_format(s, "%szone=%s", colors.param, colors.end);
> -        mf_format_subfield(&a->zone_src, s);
> -        ds_put_char(s, ',');
> +        ds_put_format(fp->s, "%szone=%s", colors.param, colors.end);
> +        mf_format_subfield(&a->zone_src, fp->s);
> +        ds_put_char(fp->s, ',');
>      } else if (a->zone_imm) {
> -        ds_put_format(s, "%szone=%s%"PRIu16",",
> +        ds_put_format(fp->s, "%szone=%s%"PRIu16",",
>                        colors.param, colors.end, a->zone_imm);
>      }
>      /* If the first action is a NAT action, format it outside of the
> 'exec'
> @@ -6318,19 +6141,19 @@ format_CT(const struct ofpact_conntrack *a,
>      const struct ofpact *action = a->actions;
>      size_t actions_len = ofpact_ct_get_action_len(a);
>      if (actions_len && action->type == OFPACT_NAT) {
> -        format_NAT(ofpact_get_NAT(action), port_map, s);
> -        ds_put_char(s, ',');
> +        format_NAT(ofpact_get_NAT(action), fp);
> +        ds_put_char(fp->s, ',');
>          actions_len -= OFPACT_ALIGN(action->len);
>          action = ofpact_next(action);
>      }
>      if (actions_len) {
> -        ds_put_format(s, "%sexec(%s", colors.paren, colors.end);
> -        ofpacts_format(action, actions_len, port_map, s);
> -        ds_put_format(s, "%s),%s", colors.paren, colors.end);
> +        ds_put_format(fp->s, "%sexec(%s", colors.paren, colors.end);
> +        ofpacts_format(action, actions_len, fp);
> +        ds_put_format(fp->s, "%s),%s", colors.paren, colors.end);
>      }
> -    format_alg(a->alg, s);
> -    ds_chomp(s, ',');
> -    ds_put_format(s, "%s)%s", colors.paren, colors.end);
> +    format_alg(a->alg, fp->s);
> +    ds_chomp(fp->s, ',');
> +    ds_put_format(fp->s, "%s)%s", colors.paren, colors.end);
>  }
>
>  /* ct_clear action. */
> @@ -6351,21 +6174,17 @@ encode_CT_CLEAR(const struct ofpact_null *null
> OVS_UNUSED,
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_CT_CLEAR(char *arg OVS_UNUSED,
> -               const struct ofputil_port_map *port_map OVS_UNUSED,
> -               struct ofpbuf *ofpacts,
> -               enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +parse_CT_CLEAR(char *arg OVS_UNUSED, const struct ofpact_parse_params *pp)
>  {
> -    ofpact_put_CT_CLEAR(ofpacts);
> +    ofpact_put_CT_CLEAR(pp->ofpacts);
>      return NULL;
>  }
>
>  static void
>  format_CT_CLEAR(const struct ofpact_null *a OVS_UNUSED,
> -                const struct ofputil_port_map *port_map OVS_UNUSED,
> -                struct ds *s)
> +                const struct ofpact_format_params *fp)
>  {
> -    ds_put_format(s, "%sct_clear%s", colors.value, colors.end);
> +    ds_put_format(fp->s, "%sct_clear%s", colors.value, colors.end);
>  }
>
>  /* NAT action. */
> @@ -6547,64 +6366,62 @@ decode_NXAST_RAW_NAT(const struct nx_action_nat
> *nan,
>  }
>
>  static void
> -format_NAT(const struct ofpact_nat *a,
> -           const struct ofputil_port_map *port_map OVS_UNUSED,
> -           struct ds *ds)
> +format_NAT(const struct ofpact_nat *a, const struct ofpact_format_params
> *fp)
>  {
> -    ds_put_format(ds, "%snat%s", colors.paren, colors.end);
> +    ds_put_format(fp->s, "%snat%s", colors.paren, colors.end);
>
>      if (a->flags & (NX_NAT_F_SRC | NX_NAT_F_DST)) {
> -        ds_put_format(ds, "%s(%s", colors.paren, colors.end);
> -        ds_put_format(ds, a->flags & NX_NAT_F_SRC ? "%ssrc%s" : "%sdst%s",
> +        ds_put_format(fp->s, "%s(%s", colors.paren, colors.end);
> +        ds_put_format(fp->s, a->flags & NX_NAT_F_SRC ? "%ssrc%s" :
> "%sdst%s",
>                        colors.param, colors.end);
>
>          if (a->range_af != AF_UNSPEC) {
> -            ds_put_format(ds, "%s=%s", colors.param, colors.end);
> +            ds_put_format(fp->s, "%s=%s", colors.param, colors.end);
>
>              if (a->range_af == AF_INET) {
> -                ds_put_format(ds, IP_FMT, IP_ARGS(a->range.addr.ipv4.
> min));
> +                ds_put_format(fp->s, IP_FMT, IP_ARGS(a->range.addr.ipv4.
> min));
>
>                  if (a->range.addr.ipv4.max
>                      && a->range.addr.ipv4.max != a->range.addr.ipv4.min) {
> -                    ds_put_format(ds, "-"IP_FMT,
> +                    ds_put_format(fp->s, "-"IP_FMT,
>                                    IP_ARGS(a->range.addr.ipv4.max));
>                  }
>              } else if (a->range_af == AF_INET6) {
> -                ipv6_format_addr_bracket(&a->range.addr.ipv6.min, ds,
> +                ipv6_format_addr_bracket(&a->range.addr.ipv6.min, fp->s,
>                                          a->range.proto.min);
>
>                  if (!ipv6_mask_is_any(&a->range.addr.ipv6.max)
>                      && memcmp(&a->range.addr.ipv6.max,
> &a->range.addr.ipv6.min,
>                                sizeof(struct in6_addr)) != 0) {
> -                    ds_put_char(ds, '-');
> -                    ipv6_format_addr_bracket(&a->range.addr.ipv6.max, ds,
> +                    ds_put_char(fp->s, '-');
> +                    ipv6_format_addr_bracket(&a->range.addr.ipv6.max,
> fp->s,
>                                              a->range.proto.min);
>                  }
>              }
>              if (a->range.proto.min) {
> -                ds_put_char(ds, ':');
> -                ds_put_format(ds, "%"PRIu16, a->range.proto.min);
> +                ds_put_char(fp->s, ':');
> +                ds_put_format(fp->s, "%"PRIu16, a->range.proto.min);
>
>                  if (a->range.proto.max
>                      && a->range.proto.max != a->range.proto.min) {
> -                    ds_put_format(ds, "-%"PRIu16, a->range.proto.max);
> +                    ds_put_format(fp->s, "-%"PRIu16, a->range.proto.max);
>                  }
>              }
> -            ds_put_char(ds, ',');
> +            ds_put_char(fp->s, ',');
>
>              if (a->flags & NX_NAT_F_PERSISTENT) {
> -                ds_put_format(ds, "%spersistent%s,",
> +                ds_put_format(fp->s, "%spersistent%s,",
>                                colors.value, colors.end);
>              }
>              if (a->flags & NX_NAT_F_PROTO_HASH) {
> -                ds_put_format(ds, "%shash%s,", colors.value, colors.end);
> +                ds_put_format(fp->s, "%shash%s,", colors.value,
> colors.end);
>              }
>              if (a->flags & NX_NAT_F_PROTO_RANDOM) {
> -                ds_put_format(ds, "%srandom%s,", colors.value,
> colors.end);
> +                ds_put_format(fp->s, "%srandom%s,", colors.value,
> colors.end);
>              }
>          }
> -        ds_chomp(ds, ',');
> -        ds_put_format(ds, "%s)%s", colors.paren, colors.end);
> +        ds_chomp(fp->s, ',');
> +        ds_put_format(fp->s, "%s)%s", colors.paren, colors.end);
>      }
>  }
>
> @@ -6668,17 +6485,14 @@ error:
>
>
>  /* Parses 'arg' as the argument to a "nat" action, and appends such an
> - * action to 'ofpacts'.
> + * action to 'pp->ofpacts'.
>   *
>   * Returns NULL if successful, otherwise a malloc()'d string describing
> the
>   * error.  The caller is responsible for freeing the returned string. */
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_NAT(char *arg,
> -          const struct ofputil_port_map *port_map OVS_UNUSED,
> -          struct ofpbuf *ofpacts,
> -          enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +parse_NAT(char *arg, const struct ofpact_parse_params *pp)
>  {
> -    struct ofpact_nat *on = ofpact_put_NAT(ofpacts);
> +    struct ofpact_nat *on = ofpact_put_NAT(pp->ofpacts);
>      char *key, *value;
>
>      on->flags = 0;
> @@ -6766,9 +6580,7 @@ encode_OUTPUT_TRUNC(const struct ofpact_output_trunc
> *output_trunc,
>
>  static char * OVS_WARN_UNUSED_RESULT
>  parse_OUTPUT_TRUNC(const char *arg,
> -                   const struct ofputil_port_map *port_map OVS_UNUSED,
> -                   struct ofpbuf *ofpacts OVS_UNUSED,
> -                   enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +                   const struct ofpact_parse_params *pp OVS_UNUSED)
>  {
>      /* Disable output_trunc parsing.  Expose as output(port=N,max_len=M)
> and
>       * reuse parse_OUTPUT to parse output_trunc action. */
> @@ -6777,11 +6589,11 @@ parse_OUTPUT_TRUNC(const char *arg,
>
>  static void
>  format_OUTPUT_TRUNC(const struct ofpact_output_trunc *a,
> -                    const struct ofputil_port_map *port_map, struct ds *s)
> +                    const struct ofpact_format_params *fp)
>  {
> -    ds_put_format(s, "%soutput%s(port=", colors.special, colors.end);
> -    ofputil_format_port(a->port, port_map, s);
> -    ds_put_format(s, ",max_len=%"PRIu32")", a->max_len);
> +    ds_put_format(fp->s, "%soutput%s(port=", colors.special, colors.end);
> +    ofputil_format_port(a->port, fp->port_map, fp->s);
> +    ds_put_format(fp->s, ",max_len=%"PRIu32")", a->max_len);
>  }
>
>
> @@ -6797,21 +6609,17 @@ encode_METER(const struct ofpact_meter *meter,
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_METER(char *arg,
> -            const struct ofputil_port_map *port_map OVS_UNUSED,
> -            struct ofpbuf *ofpacts,
> -            enum ofputil_protocol *usable_protocols)
> +parse_METER(char *arg, const struct ofpact_parse_params *pp)
>  {
> -    *usable_protocols &= OFPUTIL_P_OF13_UP;
> -    return str_to_u32(arg, &ofpact_put_METER(ofpacts)->meter_id);
> +    *pp->usable_protocols &= OFPUTIL_P_OF13_UP;
> +    return str_to_u32(arg, &ofpact_put_METER(pp->ofpacts)->meter_id);
>  }
>
>  static void
>  format_METER(const struct ofpact_meter *a,
> -             const struct ofputil_port_map *port_map OVS_UNUSED,
> -             struct ds *s)
> +             const struct ofpact_format_params *fp)
>  {
> -    ds_put_format(s, "%smeter:%s%"PRIu32,
> +    ds_put_format(fp->s, "%smeter:%s%"PRIu32,
>                    colors.param, colors.end, a->meter_id);
>  }
>
> @@ -6828,21 +6636,17 @@ encode_CLEAR_ACTIONS(const struct ofpact_null
> *null OVS_UNUSED,
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_CLEAR_ACTIONS(char *arg OVS_UNUSED,
> -                    const struct ofputil_port_map *port_map OVS_UNUSED,
> -                    struct ofpbuf *ofpacts,
> -                    enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +parse_CLEAR_ACTIONS(char *arg OVS_UNUSED, const struct
> ofpact_parse_params *pp)
>  {
> -    ofpact_put_CLEAR_ACTIONS(ofpacts);
> +    ofpact_put_CLEAR_ACTIONS(pp->ofpacts);
>      return NULL;
>  }
>
>  static void
>  format_CLEAR_ACTIONS(const struct ofpact_null *a OVS_UNUSED,
> -                     const struct ofputil_port_map *port_map OVS_UNUSED,
> -                     struct ds *s)
> +                     const struct ofpact_format_params *fp)
>  {
> -    ds_put_format(s, "%sclear_actions%s", colors.value, colors.end);
> +    ds_put_format(fp->s, "%sclear_actions%s", colors.value, colors.end);
>  }
>
>  /* Write-Actions instruction. */
> @@ -6863,17 +6667,15 @@ encode_WRITE_ACTIONS(const struct ofpact_nest
> *actions,
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_WRITE_ACTIONS(char *arg, const struct ofputil_port_map *port_map,
> -                    struct ofpbuf *ofpacts,
> -                    enum ofputil_protocol *usable_protocols)
> +parse_WRITE_ACTIONS(char *arg, const struct ofpact_parse_params *pp)
>  {
> -    size_t ofs = ofpacts_pull(ofpacts);
> +    size_t ofs = ofpacts_pull(pp->ofpacts);
>      struct ofpact_nest *on;
>      char *error;
>
>      /* Add a Write-Actions instruction and then pull it off. */
> -    ofpact_put(ofpacts, OFPACT_WRITE_ACTIONS, sizeof *on);
> -    ofpbuf_pull(ofpacts, sizeof *on);
> +    ofpact_put(pp->ofpacts, OFPACT_WRITE_ACTIONS, sizeof *on);
> +    ofpbuf_pull(pp->ofpacts, sizeof *on);
>
>      /* Parse nested actions.
>       *
> @@ -6882,26 +6684,25 @@ parse_WRITE_ACTIONS(char *arg, const struct
> ofputil_port_map *port_map,
>       * that it doesn't actually include the nested actions.  That means
> that
>       * ofpacts_parse() would reject them as being part of an
> Apply-Actions that
>       * follows a Write-Actions, which is an invalid order.  */
> -    error = ofpacts_parse(arg, port_map, ofpacts, usable_protocols, false,
> -                          OFPACT_WRITE_ACTIONS);
> +    error = ofpacts_parse(arg, pp, false, OFPACT_WRITE_ACTIONS);
>
>      /* Put the Write-Actions back on and update its length. */
> -    on = ofpbuf_push_uninit(ofpacts, sizeof *on);
> -    on->ofpact.len = ofpacts->size;
> +    on = ofpbuf_push_uninit(pp->ofpacts, sizeof *on);
> +    on->ofpact.len = pp->ofpacts->size;
>
>      /* Put any previous actions or instructions back on. */
> -    ofpbuf_push_uninit(ofpacts, ofs);
> +    ofpbuf_push_uninit(pp->ofpacts, ofs);
>
>      return error;
>  }
>
>  static void
>  format_WRITE_ACTIONS(const struct ofpact_nest *a,
> -                     const struct ofputil_port_map *port_map, struct ds
> *s)
> +                     const struct ofpact_format_params *fp)
>  {
> -    ds_put_format(s, "%swrite_actions(%s", colors.paren, colors.end);
> -    ofpacts_format(a->actions, ofpact_nest_get_action_len(a), port_map,
> s);
> -    ds_put_format(s, "%s)%s", colors.paren, colors.end);
> +    ds_put_format(fp->s, "%swrite_actions(%s", colors.paren, colors.end);
> +    ofpacts_format(a->actions, ofpact_nest_get_action_len(a), fp);
> +    ds_put_format(fp->s, "%s)%s", colors.paren, colors.end);
>  }
>
>  /* Action structure for NXAST_WRITE_METADATA.
> @@ -6956,17 +6757,14 @@ encode_WRITE_METADATA(const struct ofpact_metadata
> *metadata,
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_WRITE_METADATA(char *arg,
> -                     const struct ofputil_port_map *port_map OVS_UNUSED,
> -                     struct ofpbuf *ofpacts,
> -                     enum ofputil_protocol *usable_protocols)
> +parse_WRITE_METADATA(char *arg, const struct ofpact_parse_params *pp)
>  {
>      struct ofpact_metadata *om;
>      char *mask = strchr(arg, '/');
>
> -    *usable_protocols &= OFPUTIL_P_NXM_OF11_UP;
> +    *pp->usable_protocols &= OFPUTIL_P_NXM_OF11_UP;
>
> -    om = ofpact_put_WRITE_METADATA(ofpacts);
> +    om = ofpact_put_WRITE_METADATA(pp->ofpacts);
>      if (mask) {
>          char *error;
>
> @@ -6984,13 +6782,12 @@ parse_WRITE_METADATA(char *arg,
>
>  static void
>  format_WRITE_METADATA(const struct ofpact_metadata *a,
> -                      const struct ofputil_port_map *port_map OVS_UNUSED,
> -                      struct ds *s)
> +                      const struct ofpact_format_params *fp)
>  {
> -    ds_put_format(s, "%swrite_metadata:%s%#"PRIx64,
> +    ds_put_format(fp->s, "%swrite_metadata:%s%#"PRIx64,
>                    colors.param, colors.end, ntohll(a->metadata));
>      if (a->mask != OVS_BE64_MAX) {
> -        ds_put_format(s, "/%#"PRIx64, ntohll(a->mask));
> +        ds_put_format(fp->s, "/%#"PRIx64, ntohll(a->mask));
>      }
>  }
>
> @@ -7016,12 +6813,9 @@ encode_GOTO_TABLE(const struct ofpact_goto_table
> *goto_table,
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -parse_GOTO_TABLE(char *arg,
> -                 const struct ofputil_port_map *port_map OVS_UNUSED,
> -                 struct ofpbuf *ofpacts,
> -                 enum ofputil_protocol *usable_protocols OVS_UNUSED)
> +parse_GOTO_TABLE(char *arg, const struct ofpact_parse_params *pp)
>  {
> -    struct ofpact_goto_table *ogt = ofpact_put_GOTO_TABLE(ofpacts);
> +    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");
> @@ -7031,10 +6825,9 @@ parse_GOTO_TABLE(char *arg,
>
>  static void
>  format_GOTO_TABLE(const struct ofpact_goto_table *a,
> -                  const struct ofputil_port_map *port_map OVS_UNUSED,
> -                  struct ds *s)
> +                  const struct ofpact_format_params *fp)
>  {
> -    ds_put_format(s, "%sgoto_table:%s%"PRIu8,
> +    ds_put_format(fp->s, "%sgoto_table:%s%"PRIu8,
>                    colors.param, colors.end, a->table_id);
>  }
>
> @@ -8780,10 +8573,12 @@ ofpacts_equal_stringwise(const struct ofpact *a,
> size_t a_len,
>                           const struct ofpact *b, size_t b_len)
>  {
>      struct ds a_s = DS_EMPTY_INITIALIZER;
> -    struct ds b_s = DS_EMPTY_INITIALIZER;
> +    struct ofpact_format_params a_fp = { .s = &a_s };
> +    ofpacts_format(a, a_len, &a_fp);
>
> -    ofpacts_format(a, a_len, NULL, &a_s);
> -    ofpacts_format(b, b_len, NULL, &b_s);
> +    struct ds b_s = DS_EMPTY_INITIALIZER;
> +    struct ofpact_format_params b_fp = { .s = &b_s };
> +    ofpacts_format(b, b_len, &b_fp);
>
>      bool equal = !strcmp(ds_cstr(&a_s), ds_cstr(&b_s));
>
> @@ -8821,13 +8616,12 @@ ofpacts_get_meter(const struct ofpact ofpacts[],
> size_t ofpacts_len)
>
>  static void
>  ofpact_format(const struct ofpact *a,
> -              const struct ofputil_port_map *port_map, struct ds *s)
> +              const struct ofpact_format_params *fp)
>  {
>      switch (a->type) {
>  #define OFPACT(ENUM, STRUCT, MEMBER, NAME)                              \
>          case OFPACT_##ENUM:                                             \
> -            format_##ENUM(ALIGNED_CAST(const struct STRUCT *, a),       \
> -                          port_map, s);                               \
> +            format_##ENUM(ALIGNED_CAST(const struct STRUCT *, a), fp);  \
>              break;
>          OFPACTS
>  #undef OFPACT
> @@ -8837,23 +8631,23 @@ ofpact_format(const struct ofpact *a,
>  }
>
>  /* Appends a string representing the 'ofpacts_len' bytes of ofpacts in
> - * 'ofpacts' to 'string'.  If 'port_map' is nonnull, uses it to translate
> - * port numbers to names in output. */
> + * 'ofpacts' to 'fp->s'.  If 'port_map' is nonnull, uses it to translate
> port
> + * numbers to names in output. */
>  void
>  ofpacts_format(const struct ofpact *ofpacts, size_t ofpacts_len,
> -               const struct ofputil_port_map *port_map, struct ds *string)
> +               const struct ofpact_format_params *fp)
>  {
>      if (!ofpacts_len) {
> -        ds_put_format(string, "%sdrop%s", colors.drop, colors.end);
> +        ds_put_format(fp->s, "%sdrop%s", colors.drop, colors.end);
>      } else {
>          const struct ofpact *a;
>
>          OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
>              if (a != ofpacts) {
> -                ds_put_char(string, ',');
> +                ds_put_char(fp->s, ',');
>              }
>
> -            ofpact_format(a, port_map, string);
> +            ofpact_format(a, fp);
>          }
>      }
>  }
> @@ -8907,13 +8701,12 @@ ofpact_finish(struct ofpbuf *ofpacts, struct
> ofpact *ofpact)
>
>  static char * OVS_WARN_UNUSED_RESULT
>  ofpact_parse(enum ofpact_type type, char *value,
> -             const struct ofputil_port_map *port_map, struct ofpbuf
> *ofpacts,
> -             enum ofputil_protocol *usable_protocols)
> +             const struct ofpact_parse_params *pp)
>  {
>      switch (type) {
>  #define OFPACT(ENUM, STRUCT, MEMBER, NAME)                              \
>          case OFPACT_##ENUM:                                             \
> -            return parse_##ENUM(value, port_map, ofpacts,
> usable_protocols);
> +            return parse_##ENUM(value, pp);
>          OFPACTS
>  #undef OFPACT
>      default:
> @@ -8943,9 +8736,7 @@ ofpact_type_from_name(const char *name, enum
> ofpact_type *type)
>   * If 'outer_action' is specified, indicates that the actions being parsed
>   * are nested within another action of the type specified in
> 'outer_action'. */
>  static char * OVS_WARN_UNUSED_RESULT
> -ofpacts_parse__(char *str, const struct ofputil_port_map *port_map,
> -                struct ofpbuf *ofpacts,
> -                enum ofputil_protocol *usable_protocols,
> +ofpacts_parse__(char *str, const struct ofpact_parse_params *pp,
>                  bool allow_instructions, enum ofpact_type outer_action)
>  {
>      int prev_inst = -1;
> @@ -8962,31 +8753,28 @@ ofpacts_parse__(char *str, const struct
> ofputil_port_map *port_map,
>          ofp_port_t port;
>
>          if (ofpact_type_from_name(key, &type)) {
> -            error = ofpact_parse(type, value, port_map,
> -                                 ofpacts, usable_protocols);
> +            error = ofpact_parse(type, value, pp);
>              inst = ovs_instruction_type_from_ofpact_type(type);
>          } else if (!strcasecmp(key, "mod_vlan_vid")) {
> -            error = parse_set_vlan_vid(value, ofpacts, true);
> +            error = parse_set_vlan_vid(value, true, pp);
>          } else if (!strcasecmp(key, "mod_vlan_pcp")) {
> -            error = parse_set_vlan_pcp(value, ofpacts, true);
> +            error = parse_set_vlan_pcp(value, true, pp);
>          } else if (!strcasecmp(key, "set_nw_ttl")) {
> -            error = parse_SET_IP_TTL(value, port_map,
> -                                     ofpacts, usable_protocols);
> +            error = parse_SET_IP_TTL(value, pp);
>          } else if (!strcasecmp(key, "pop_vlan")) {
> -            error = parse_pop_vlan(ofpacts);
> +            error = parse_pop_vlan(pp);
>          } else if (!strcasecmp(key, "set_tunnel64")) {
> -            error = parse_set_tunnel(value, ofpacts,
> -                                     NXAST_RAW_SET_TUNNEL64);
> +            error = parse_set_tunnel(value, NXAST_RAW_SET_TUNNEL64, pp);
>          } else if (!strcasecmp(key, "load")) {
> -            error = parse_reg_load(value, ofpacts);
> +            error = parse_reg_load(value, pp);
>          } else if (!strcasecmp(key, "bundle_load")) {
> -            error = parse_bundle_load(value, port_map, ofpacts);
> +            error = parse_bundle_load(value, pp);
>          } else if (!strcasecmp(key, "drop")) {
>              drop = true;
>          } else if (!strcasecmp(key, "apply_actions")) {
>              return xstrdup("apply_actions is the default instruction");
> -        } else if (ofputil_port_from_string(key, port_map, &port)) {
> -            ofpact_put_OUTPUT(ofpacts)->port = port;
> +        } else if (ofputil_port_from_string(key, pp->port_map, &port)) {
> +            ofpact_put_OUTPUT(pp->ofpacts)->port = port;
>          } else {
>              return xasprintf("unknown action %s", key);
>          }
> @@ -9013,12 +8801,12 @@ ofpacts_parse__(char *str, const struct
> ofputil_port_map *port_map,
>          prev_inst = inst;
>      }
>
> -    if (drop && ofpacts->size) {
> +    if (drop && pp->ofpacts->size) {
>          return xstrdup("\"drop\" must not be accompanied by any other
> action "
>                         "or instruction");
>      }
>
> -    retval = ofpacts_verify(ofpacts->data, ofpacts->size,
> +    retval = ofpacts_verify(pp->ofpacts->data, pp->ofpacts->size,
>                              (allow_instructions
>                               ? (1u << N_OVS_INSTRUCTIONS) - 1
>                               : 1u << OVSINST_OFPIT11_APPLY_ACTIONS),
> @@ -9031,32 +8819,27 @@ ofpacts_parse__(char *str, const struct
> ofputil_port_map *port_map,
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -ofpacts_parse(char *str, const struct ofputil_port_map *port_map,
> -              struct ofpbuf *ofpacts, enum ofputil_protocol
> *usable_protocols,
> +ofpacts_parse(char *str, const struct ofpact_parse_params *pp,
>                bool allow_instructions, enum ofpact_type outer_action)
>  {
> -    uint32_t orig_size = ofpacts->size;
> -    char *error = ofpacts_parse__(str, port_map, ofpacts,
> usable_protocols,
> -                                  allow_instructions, outer_action);
> +    uint32_t orig_size = pp->ofpacts->size;
> +    char *error = ofpacts_parse__(str, pp, allow_instructions,
> outer_action);
>      if (error) {
> -        ofpacts->size = orig_size;
> +        pp->ofpacts->size = orig_size;
>      }
>      return error;
>  }
>
>  static char * OVS_WARN_UNUSED_RESULT
> -ofpacts_parse_copy(const char *s_, const struct ofputil_port_map
> *port_map,
> -                   struct ofpbuf *ofpacts,
> -                   enum ofputil_protocol *usable_protocols,
> +ofpacts_parse_copy(const char *s_, const struct ofpact_parse_params *pp,
>                     bool allow_instructions, enum ofpact_type outer_action)
>  {
>      char *error, *s;
>
> -    *usable_protocols = OFPUTIL_P_ANY;
> +    *pp->usable_protocols = OFPUTIL_P_ANY;
>
>      s = xstrdup(s_);
> -    error = ofpacts_parse(s, port_map, ofpacts, usable_protocols,
> -                          allow_instructions, outer_action);
> +    error = ofpacts_parse(s, pp, allow_instructions, outer_action);
>      free(s);
>
>      return error;
> @@ -9069,12 +8852,9 @@ ofpacts_parse_copy(const char *s_, const struct
> ofputil_port_map *port_map,
>   * Returns NULL if successful, otherwise a malloc()'d string describing
> the
>   * error.  The caller is responsible for freeing the returned string. */
>  char * OVS_WARN_UNUSED_RESULT
> -ofpacts_parse_actions(const char *s, const struct ofputil_port_map
> *port_map,
> -                      struct ofpbuf *ofpacts,
> -                      enum ofputil_protocol *usable_protocols)
> +ofpacts_parse_actions(const char *s, const struct ofpact_parse_params *pp)
>  {
> -    return ofpacts_parse_copy(s, port_map, ofpacts, usable_protocols,
> -                              false, 0);
> +    return ofpacts_parse_copy(s, pp, false, 0);
>  }
>
>  /* Parses 's' as a set of OpenFlow instructions and appends the
> instructions to
> @@ -9083,12 +8863,9 @@ ofpacts_parse_actions(const char *s, const struct
> ofputil_port_map *port_map,
>   * Returns NULL if successful, otherwise a malloc()'d string describing
> the
>   * error.  The caller is responsible for freeing the returned string. */
>  char * OVS_WARN_UNUSED_RESULT
> -ofpacts_parse_instructions(const char *s,
> -                           const struct ofputil_port_map *port_map,
> -                           struct ofpbuf *ofpacts,
> -                           enum ofputil_protocol *usable_protocols)
> +ofpacts_parse_instructions(const char *s, const struct
> ofpact_parse_params *pp)
>  {
> -    return ofpacts_parse_copy(s, port_map, ofpacts, usable_protocols,
> true, 0);
> +    return ofpacts_parse_copy(s, pp, true, 0);
>  }
>
>  const char *
> diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c
> index bacbaa63886e..b68081740f09 100644
> --- a/lib/ofp-parse.c
> +++ b/lib/ofp-parse.c
> @@ -557,8 +557,12 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int
> command, char *string,
>          char *error;
>
>          ofpbuf_init(&ofpacts, 32);
> -        error = ofpacts_parse_instructions(act_str, port_map, &ofpacts,
> -                                           &action_usable_protocols);
> +        struct ofpact_parse_params pp = {
> +            .port_map = port_map,
> +            .ofpacts = &ofpacts,
> +            .usable_protocols = &action_usable_protocols
> +        };
> +        error = ofpacts_parse_instructions(act_str, &pp);
>          *usable_protocols &= action_usable_protocols;
>          if (!error) {
>              enum ofperr err;
> @@ -713,8 +717,12 @@ parse_ofp_packet_out_str__(struct ofputil_packet_out
> *po, char *string,
>      }
>
>      if (act_str) {
> -        error = ofpacts_parse_actions(act_str, port_map, &ofpacts,
> -                                      &action_usable_protocols);
> +        struct ofpact_parse_params pp = {
> +            .port_map = port_map,
> +            .ofpacts = &ofpacts,
> +            .usable_protocols = &action_usable_protocols,
> +        };
> +        error = ofpacts_parse_actions(act_str, &pp);
>          *usable_protocols &= action_usable_protocols;
>          if (error) {
>              goto out;
> @@ -1487,8 +1495,12 @@ parse_bucket_str(struct ofputil_bucket *bucket,
> char *str_,
>      ds_chomp(&actions, ',');
>
>      ofpbuf_init(&ofpacts, 0);
> -    error = ofpacts_parse_actions(ds_cstr(&actions), port_map, &ofpacts,
> -                                  usable_protocols);
> +    struct ofpact_parse_params pp = {
> +        .port_map = port_map,
> +        .ofpacts = &ofpacts,
> +        .usable_protocols = usable_protocols,
> +    };
> +    error = ofpacts_parse_actions(ds_cstr(&actions), &pp);
>      ds_destroy(&actions);
>      if (error) {
>          ofpbuf_uninit(&ofpacts);
> diff --git a/lib/ofp-print.c b/lib/ofp-print.c
> index 8a1f426ade36..639d3bddf36d 100644
> --- a/lib/ofp-print.c
> +++ b/lib/ofp-print.c
> @@ -205,15 +205,20 @@ ofp_print_packet_in(struct ds *string, const struct
> ofp_header *oh,
>          ds_put_cstr(string, " continuation.conntracked=true\n");
>      }
>
> +    struct ofpact_format_params fp = {
> +        .port_map = port_map,
> +        .s = string,
> +    };
> +
>      if (pin.actions_len) {
>          ds_put_cstr(string, " continuation.actions=");
> -        ofpacts_format(pin.actions, pin.actions_len, port_map, string);
> +        ofpacts_format(pin.actions, pin.actions_len, &fp);
>          ds_put_char(string, '\n');
>      }
>
>      if (pin.action_set_len) {
>          ds_put_cstr(string, " continuation.action_set=");
> -        ofpacts_format(pin.action_set, pin.action_set_len, port_map,
> string);
> +        ofpacts_format(pin.action_set, pin.action_set_len, &fp);
>          ds_put_char(string, '\n');
>      }
>
> @@ -252,7 +257,11 @@ ofp_print_packet_out(struct ds *string, const struct
> ofp_header *oh,
>      match_format(&po.flow_metadata, port_map, string,
> OFP_DEFAULT_PRIORITY);
>
>      ds_put_cstr(string, " actions=");
> -    ofpacts_format(po.ofpacts, po.ofpacts_len, port_map, string);
> +    struct ofpact_format_params fp = {
> +        .port_map = port_map,
> +        .s = string,
> +    };
> +    ofpacts_format(po.ofpacts, po.ofpacts_len, &fp);
>
>      if (po.buffer_id == UINT32_MAX) {
>          ds_put_format(string, " data_len=%"PRIuSIZE, po.packet_len);
> @@ -916,7 +925,11 @@ ofp_print_flow_mod(struct ds *s, const struct
> ofp_header *oh,
>      ofp_print_flow_flags(s, fm.flags);
>
>      ds_put_cstr(s, "actions=");
> -    ofpacts_format(fm.ofpacts, fm.ofpacts_len, port_map, s);
> +    struct ofpact_format_params fp = {
> +        .port_map = port_map,
> +        .s = s,
> +    };
> +    ofpacts_format(fm.ofpacts, fm.ofpacts_len, &fp);
>      ofpbuf_uninit(&ofpacts);
>
>      return 0;
> @@ -1749,7 +1762,11 @@ ofp_print_flow_stats(struct ds *string, const
> struct ofputil_flow_stats *fs,
>      }
>
>      ds_put_format(string, "%sactions=%s", colors.actions, colors.end);
> -    ofpacts_format(fs->ofpacts, fs->ofpacts_len, port_map, string);
> +    struct ofpact_format_params fp = {
> +        .port_map = port_map,
> +        .s = string,
> +    };
> +    ofpacts_format(fs->ofpacts, fs->ofpacts_len, &fp);
>  }
>
>  static enum ofperr
> @@ -2544,8 +2561,11 @@ ofp_print_nxst_flow_monitor_reply(struct ds
> *string,
>                  ds_put_char(string, ' ');
>              }
>              ds_put_cstr(string, "actions=");
> -            ofpacts_format(update.ofpacts, update.ofpacts_len, port_map,
> -                           string);
> +            struct ofpact_format_params fp = {
> +                .port_map = port_map,
> +                .s = string,
> +            };
> +            ofpacts_format(update.ofpacts, update.ofpacts_len, &fp);
>          }
>      }
>  }
> @@ -2676,7 +2696,11 @@ ofp_print_group(struct ds *s, uint32_t group_id,
> uint8_t type,
>          }
>
>          ds_put_cstr(s, "actions=");
> -        ofpacts_format(bucket->ofpacts, bucket->ofpacts_len, port_map,
> s);
> +        struct ofpact_format_params fp = {
> +            .port_map = port_map,
> +            .s = s,
> +        };
> +        ofpacts_format(bucket->ofpacts, bucket->ofpacts_len, &fp);
>          ds_put_char(s, ',');
>      }
>
> diff --git a/ofproto/ofproto-dpif-trace.c b/ofproto/ofproto-dpif-trace.c
> index 4999d1d6f326..7b29013db99a 100644
> --- a/ofproto/ofproto-dpif-trace.c
> +++ b/ofproto/ofproto-dpif-trace.c
> @@ -463,8 +463,12 @@ ofproto_unixctl_trace_actions(struct unixctl_conn
> *conn, int argc,
>      ofpbuf_init(&ofpacts, 0);
>
>      /* Parse actions. */
> -    error = ofpacts_parse_actions(argv[--argc], NULL,
> -                                  &ofpacts, &usable_protocols);
> +    struct ofpact_parse_params pp = {
> +        .port_map = NULL,
> +        .ofpacts = &ofpacts,
> +        .usable_protocols = &usable_protocols,
> +    };
> +    error = ofpacts_parse_actions(argv[--argc], &pp);
>      if (error) {
>          unixctl_command_reply_error(conn, error);
>          free(error);
> diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
> index 896d5e290a46..fa9a89334163 100644
> --- a/ofproto/ofproto-dpif-xlate.c
> +++ b/ofproto/ofproto-dpif-xlate.c
> @@ -693,7 +693,8 @@ xlate_report_actions(const struct xlate_ctx *ctx, enum
> oftrace_node_type type,
>      if (OVS_UNLIKELY(ctx->xin->trace)) {
>          struct ds s = DS_EMPTY_INITIALIZER;
>          ds_put_format(&s, "%s: ", title);
> -        ofpacts_format(ofpacts, ofpacts_len, NULL, &s);
> +        struct ofpact_format_params fp = { .s = &s };
> +        ofpacts_format(ofpacts, ofpacts_len, &fp);
>          oftrace_report(ctx->xin->trace, type, ds_cstr(&s));
>          ds_destroy(&s);
>      }
> @@ -714,7 +715,8 @@ xlate_report_action_set(const struct xlate_ctx *ctx,
> const char *verb)
>          ofpacts_execute_action_set(&action_list, &ctx->action_set);
>          if (action_list.size) {
>              struct ds s = DS_EMPTY_INITIALIZER;
> -            ofpacts_format(action_list.data, action_list.size, NULL, &s);
> +            struct ofpact_format_params fp = { .s = &s };
> +            ofpacts_format(action_list.data, action_list.size, &fp);
>              xlate_report(ctx, OFT_DETAIL, "action set %s: %s",
>                           verb, ds_cstr(&s));
>              ds_destroy(&s);
> @@ -4978,7 +4980,8 @@ xlate_learn_action(struct xlate_ctx *ctx, const
> struct ofpact_learn *learn)
>                  ds_put_cstr(&s, " send_flow_rem");
>              }
>              ds_put_cstr(&s, " actions=");
> -            ofpacts_format(fm.ofpacts, fm.ofpacts_len, NULL, &s);
> +            struct ofpact_format_params fp = { .s = &s };
> +            ofpacts_format(fm.ofpacts, fm.ofpacts_len, &fp);
>              xlate_report(ctx, OFT_DETAIL, "%s", ds_cstr(&s));
>              ds_destroy(&s);
>          }
> @@ -6087,7 +6090,8 @@ do_xlate_actions(const struct ofpact *ofpacts,
> size_t ofpacts_len,
>
>          if (OVS_UNLIKELY(ctx->xin->trace)) {
>              struct ds s = DS_EMPTY_INITIALIZER;
> -            ofpacts_format(a, OFPACT_ALIGN(a->len), NULL, &s);
> +            struct ofpact_format_params fp = { .s = &s };
> +            ofpacts_format(a, OFPACT_ALIGN(a->len), &fp);
>              xlate_report(ctx, OFT_ACTION, "%s", ds_cstr(&s));
>              ds_destroy(&s);
>          }
> diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
> index d42acd747acc..71f449f7ddda 100644
> --- a/ofproto/ofproto.c
> +++ b/ofproto/ofproto.c
> @@ -4399,7 +4399,8 @@ flow_stats_ds(struct ofproto *ofproto, struct rule
> *rule, struct ds *results)
>      ds_put_char(results, ',');
>
>      ds_put_cstr(results, "actions=");
> -    ofpacts_format(actions->ofpacts, actions->ofpacts_len, NULL,
> results);
> +    struct ofpact_format_params fp = { .s = results };
> +    ofpacts_format(actions->ofpacts, actions->ofpacts_len, &fp);
>
>      ds_put_cstr(results, "\n");
>  }
> diff --git a/ovn/controller/ofctrl.c b/ovn/controller/ofctrl.c
> index 2fa980ebdae1..264667769296 100644
> --- a/ovn/controller/ofctrl.c
> +++ b/ovn/controller/ofctrl.c
> @@ -685,7 +685,8 @@ ovn_flow_to_string(const struct ovn_flow *f)
>      ds_put_format(&s, "priority=%"PRIu16", ", f->priority);
>      match_format(&f->match, NULL, &s, OFP_DEFAULT_PRIORITY);
>      ds_put_cstr(&s, ", actions=");
> -    ofpacts_format(f->ofpacts, f->ofpacts_len, NULL, &s);
> +    struct ofpact_format_params fp = { .s = &s };
> +    ofpacts_format(f->ofpacts, f->ofpacts_len, &fp);
>      return ds_steal_cstr(&s);
>  }
>
> diff --git a/ovn/utilities/ovn-sbctl.c b/ovn/utilities/ovn-sbctl.c
> index 1c2e6843f23c..0cb8ea480438 100644
> --- a/ovn/utilities/ovn-sbctl.c
> +++ b/ovn/utilities/ovn-sbctl.c
> @@ -806,7 +806,8 @@ sbctl_dump_openflow(struct vconn *vconn, const struct
> uuid *uuid, bool stats)
>                  }
>
>                  ds_put_format(&s, "%sactions=%s", colors.actions,
> colors.end);
> -                ofpacts_format(fs->ofpacts, fs->ofpacts_len, NULL, &s);
> +                struct ofpact_format_params fp = { .s = &s };
> +                ofpacts_format(fs->ofpacts, fs->ofpacts_len, &fp);
>              }
>              printf("    %s\n", ds_cstr(&s));
>          }
> diff --git a/tests/test-ovn.c b/tests/test-ovn.c
> index f9a5085f7185..89b46dbff6fc 100644
> --- a/tests/test-ovn.c
> +++ b/tests/test-ovn.c
> @@ -1263,7 +1263,8 @@ test_parse_actions(struct ovs_cmdl_context *ctx
> OVS_UNUSED)
>              ofpbuf_init(&ofpacts, 0);
>              ovnacts_encode(ovnacts.data, ovnacts.size, &ep, &ofpacts);
>              struct ds ofpacts_s = DS_EMPTY_INITIALIZER;
> -            ofpacts_format(ofpacts.data, ofpacts.size, NULL, &ofpacts_s);
> +            struct ofpact_format_params fp = { .s = &ofpacts_s };
> +            ofpacts_format(ofpacts.data, ofpacts.size, &fp);
>              printf("    encodes as %s\n", ds_cstr(&ofpacts_s));
>              ds_destroy(&ofpacts_s);
>              ofpbuf_uninit(&ofpacts);
> diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
> index 953184da4d19..56af5a444d95 100644
> --- a/utilities/ovs-ofctl.c
> +++ b/utilities/ovs-ofctl.c
> @@ -2194,9 +2194,12 @@ ofctl_packet_out(struct ovs_cmdl_context *ctx)
>          int i;
>
>          ofpbuf_init(&ofpacts, 64);
> -        error = ofpacts_parse_actions(ctx->argv[3],
> -                                      ports_to_accept(ctx->argv[1]),
> &ofpacts,
> -                                      &usable_protocols);
> +        struct ofpact_parse_params pp = {
> +            .port_map = ports_to_accept(ctx->argv[1]),
> +            .ofpacts = &ofpacts,
> +            .usable_protocols = &usable_protocols
> +        };
> +        error = ofpacts_parse_actions(ctx->argv[3], &pp);
>          if (error) {
>              ovs_fatal(0, "%s", error);
>          }
> @@ -3181,8 +3184,11 @@ fte_version_format(const struct fte_state
> *fte_state, const struct fte *fte,
>      }
>
>      ds_put_cstr(s, " actions=");
> -    ofpacts_format(version->ofpacts, version->ofpacts_len,
> -                   fte_state->port_map, s);
> +    struct ofpact_format_params fp = {
> +        .port_map = fte_state->port_map,
> +        .s = s,
> +    };
> +    ofpacts_format(version->ofpacts, version->ofpacts_len, &fp);
>
>      ds_put_char(s, '\n');
>  }
> @@ -4038,7 +4044,8 @@ ofctl_parse_actions__(const char *version_s, bool
> instructions)
>          /* Print cls_rule. */
>          ds_init(&s);
>          ds_put_cstr(&s, "actions=");
> -        ofpacts_format(ofpacts.data, ofpacts.size, NULL, &s);
> +        struct ofpact_format_params fp = { .s = &s };
> +        ofpacts_format(ofpacts.data, ofpacts.size, &fp);
>          puts(ds_cstr(&s));
>          ds_destroy(&s);
>
> --
> 2.10.2
>
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>

Patch

diff --git a/include/openvswitch/ofp-actions.h b/include/openvswitch/ofp-actions.h
index 4e957358f90a..454c705ccf73 100644
--- a/include/openvswitch/ofp-actions.h
+++ b/include/openvswitch/ofp-actions.h
@@ -1064,18 +1064,32 @@  bool ofpacts_equal_stringwise(const struct ofpact a[], size_t a_len,
 const struct mf_field *ofpact_get_mf_dst(const struct ofpact *ofpact);
 uint32_t ofpacts_get_meter(const struct ofpact[], size_t ofpacts_len);
 
-/* Formatting and parsing ofpacts. */
+/* Formatting ofpacts. */
+struct ofpact_format_params {
+    /* Input. */
+    const struct ofputil_port_map *port_map;
+
+    /* Output. */
+    struct ds *s;
+};
 void ofpacts_format(const struct ofpact[], size_t ofpacts_len,
-                    const struct ofputil_port_map *, struct ds *);
-char *ofpacts_parse_actions(const char *, const struct ofputil_port_map *,
-                            struct ofpbuf *ofpacts,
-                            enum ofputil_protocol *usable_protocols)
+                    const struct ofpact_format_params *);
+const char *ofpact_name(enum ofpact_type);
+
+/* Parsing ofpacts. */
+struct ofpact_parse_params {
+    /* Input. */
+    const struct ofputil_port_map *port_map;
+
+    /* Output. */
+    struct ofpbuf *ofpacts;
+    enum ofputil_protocol *usable_protocols;
+};
+char *ofpacts_parse_actions(const char *, const struct ofpact_parse_params *)
     OVS_WARN_UNUSED_RESULT;
-char *ofpacts_parse_instructions(const char *, const struct ofputil_port_map *,
-                                 struct ofpbuf *ofpacts,
-                                 enum ofputil_protocol *usable_protocols)
+char *ofpacts_parse_instructions(const char *,
+                                 const struct ofpact_parse_params *)
     OVS_WARN_UNUSED_RESULT;
-const char *ofpact_name(enum ofpact_type);
 
 /* Internal use by the helpers below. */
 void ofpact_init(struct ofpact *, enum ofpact_type, size_t len);
diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
index db933634bf8b..93792ddfca4b 100644
--- a/lib/ofp-actions.c
+++ b/lib/ofp-actions.c
@@ -417,8 +417,7 @@  static void *ofpact_put_raw(struct ofpbuf *, enum ofp_version,
                             enum ofp_raw_action_type, uint64_t arg);
 
 static char *OVS_WARN_UNUSED_RESULT ofpacts_parse(
-    char *str, const struct ofputil_port_map *,
-    struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols,
+    char *str, const struct ofpact_parse_params *pp,
     bool allow_instructions, enum ofpact_type outer_action);
 static enum ofperr ofpacts_pull_openflow_actions__(
     struct ofpbuf *openflow, unsigned int actions_len,
@@ -426,8 +425,7 @@  static enum ofperr ofpacts_pull_openflow_actions__(
     struct ofpbuf *ofpacts, enum ofpact_type outer_action,
     const struct vl_mff_map *vl_mff_map, uint64_t *ofpacts_tlv_bitmap);
 static char * OVS_WARN_UNUSED_RESULT ofpacts_parse_copy(
-    const char *s_, const struct ofputil_port_map *, struct ofpbuf *ofpacts,
-    enum ofputil_protocol *usable_protocols,
+    const char *s_, const struct ofpact_parse_params *pp,
     bool allow_instructions, enum ofpact_type outer_action);
 
 /* Returns the ofpact following 'ofpact', except that if 'ofpact' contains
@@ -607,16 +605,16 @@  encode_OUTPUT(const struct ofpact_output *output,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_truncate_subfield(struct ofpact_output_trunc *output_trunc,
-                        const char *arg_,
-                        const struct ofputil_port_map *port_map)
+parse_truncate_subfield(const char *arg_,
+                        const struct ofpact_parse_params *pp,
+                        struct ofpact_output_trunc *output_trunc)
 {
     char *key, *value;
     char *arg = CONST_CAST(char *, arg_);
 
     while (ofputil_parse_key_value(&arg, &key, &value)) {
         if (!strcmp(key, "port")) {
-            if (!ofputil_port_from_string(value, port_map,
+            if (!ofputil_port_from_string(value, pp->port_map,
                                           &output_trunc->port)) {
                 return xasprintf("output to unknown truncate port: %s",
                                   value);
@@ -643,21 +641,18 @@  parse_truncate_subfield(struct ofpact_output_trunc *output_trunc,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_OUTPUT(const char *arg,
-             const struct ofputil_port_map *port_map,
-             struct ofpbuf *ofpacts,
-             enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_OUTPUT(const char *arg, const struct ofpact_parse_params *pp)
 {
     if (strstr(arg, "port") && strstr(arg, "max_len")) {
         struct ofpact_output_trunc *output_trunc;
 
-        output_trunc = ofpact_put_OUTPUT_TRUNC(ofpacts);
-        return parse_truncate_subfield(output_trunc, arg, port_map);
+        output_trunc = ofpact_put_OUTPUT_TRUNC(pp->ofpacts);
+        return parse_truncate_subfield(arg, pp, output_trunc);
     }
 
     ofp_port_t port;
-    if (ofputil_port_from_string(arg, port_map, &port)) {
-        struct ofpact_output *output = ofpact_put_OUTPUT(ofpacts);
+    if (ofputil_port_from_string(arg, pp->port_map, &port)) {
+        struct ofpact_output *output = ofpact_put_OUTPUT(pp->ofpacts);
         output->port = port;
         output->max_len = output->port == OFPP_CONTROLLER ? UINT16_MAX : 0;
         return NULL;
@@ -668,7 +663,7 @@  parse_OUTPUT(const char *arg,
     if (!error) {
         struct ofpact_output_reg *output_reg;
 
-        output_reg = ofpact_put_OUTPUT_REG(ofpacts);
+        output_reg = ofpact_put_OUTPUT_REG(pp->ofpacts);
         output_reg->max_len = UINT16_MAX;
         output_reg->src = src;
         return NULL;
@@ -680,14 +675,14 @@  parse_OUTPUT(const char *arg,
 
 static void
 format_OUTPUT(const struct ofpact_output *a,
-              const struct ofputil_port_map *port_map, struct ds *s)
+              const struct ofpact_format_params *fp)
 {
     if (ofp_to_u16(a->port) < ofp_to_u16(OFPP_MAX)) {
-        ds_put_format(s, "%soutput:%s", colors.special, colors.end);
+        ds_put_format(fp->s, "%soutput:%s", colors.special, colors.end);
     }
-    ofputil_format_port(a->port, port_map, s);
+    ofputil_format_port(a->port, fp->port_map, fp->s);
     if (a->port == OFPP_CONTROLLER) {
-        ds_put_format(s, ":%"PRIu16, a->max_len);
+        ds_put_format(fp->s, ":%"PRIu16, a->max_len);
     }
 }
 
@@ -710,20 +705,16 @@  encode_GROUP(const struct ofpact_group *group,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_GROUP(char *arg,
-            const struct ofputil_port_map *port_map OVS_UNUSED,
-            struct ofpbuf *ofpacts,
-            enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_GROUP(char *arg, const struct ofpact_parse_params *pp)
 {
-    return str_to_u32(arg, &ofpact_put_GROUP(ofpacts)->group_id);
+    return str_to_u32(arg, &ofpact_put_GROUP(pp->ofpacts)->group_id);
 }
 
 static void
 format_GROUP(const struct ofpact_group *a,
-             const struct ofputil_port_map *port_map OVS_UNUSED,
-             struct ds *s)
+             const struct ofpact_format_params *fp)
 {
-    ds_put_format(s, "%sgroup:%s%"PRIu32,
+    ds_put_format(fp->s, "%sgroup:%s%"PRIu32,
                   colors.special, colors.end, a->group_id);
 }
 
@@ -891,10 +882,7 @@  encode_CONTROLLER(const struct ofpact_controller *controller,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_CONTROLLER(char *arg,
-                 const struct ofputil_port_map *port_map OVS_UNUSED,
-                 struct ofpbuf *ofpacts,
-                 enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_CONTROLLER(char *arg, const struct ofpact_parse_params *pp)
 {
     enum ofp_packet_in_reason reason = OFPR_ACTION;
     uint16_t controller_id = 0;
@@ -941,30 +929,30 @@  parse_CONTROLLER(char *arg,
     if (reason == OFPR_ACTION && controller_id == 0 && !userdata && !pause) {
         struct ofpact_output *output;
 
-        output = ofpact_put_OUTPUT(ofpacts);
+        output = ofpact_put_OUTPUT(pp->ofpacts);
         output->port = OFPP_CONTROLLER;
         output->max_len = max_len;
     } else {
         struct ofpact_controller *controller;
 
-        controller = ofpact_put_CONTROLLER(ofpacts);
+        controller = ofpact_put_CONTROLLER(pp->ofpacts);
         controller->max_len = max_len;
         controller->reason = reason;
         controller->controller_id = controller_id;
         controller->pause = pause;
 
         if (userdata) {
-            size_t start_ofs = ofpacts->size;
-            const char *end = ofpbuf_put_hex(ofpacts, userdata, NULL);
+            size_t start_ofs = pp->ofpacts->size;
+            const char *end = ofpbuf_put_hex(pp->ofpacts, userdata, NULL);
             if (*end) {
                 return xstrdup("bad hex digit in `controller' "
                                "action `userdata'");
             }
-            size_t userdata_len = ofpacts->size - start_ofs;
-            controller = ofpacts->header;
+            size_t userdata_len = pp->ofpacts->size - start_ofs;
+            controller = pp->ofpacts->header;
             controller->userdata_len = userdata_len;
         }
-        ofpact_finish_CONTROLLER(ofpacts, &controller);
+        ofpact_finish_CONTROLLER(pp->ofpacts, &controller);
     }
 
     return NULL;
@@ -983,42 +971,41 @@  format_hex_arg(struct ds *s, const uint8_t *data, size_t len)
 
 static void
 format_CONTROLLER(const struct ofpact_controller *a,
-                  const struct ofputil_port_map *port_map OVS_UNUSED,
-                  struct ds *s)
+                  const struct ofpact_format_params *fp)
 {
     if (a->reason == OFPR_ACTION && !a->controller_id && !a->userdata_len
         && !a->pause) {
-        ds_put_format(s, "%sCONTROLLER:%s%"PRIu16,
+        ds_put_format(fp->s, "%sCONTROLLER:%s%"PRIu16,
                       colors.special, colors.end, a->max_len);
     } else {
         enum ofp_packet_in_reason reason = a->reason;
 
-        ds_put_format(s, "%scontroller(%s", colors.paren, colors.end);
+        ds_put_format(fp->s, "%scontroller(%s", colors.paren, colors.end);
         if (reason != OFPR_ACTION) {
             char reasonbuf[OFPUTIL_PACKET_IN_REASON_BUFSIZE];
 
-            ds_put_format(s, "%sreason=%s%s,", colors.param, colors.end,
+            ds_put_format(fp->s, "%sreason=%s%s,", colors.param, colors.end,
                           ofputil_packet_in_reason_to_string(
                               reason, reasonbuf, sizeof reasonbuf));
         }
         if (a->max_len != UINT16_MAX) {
-            ds_put_format(s, "%smax_len=%s%"PRIu16",",
+            ds_put_format(fp->s, "%smax_len=%s%"PRIu16",",
                           colors.param, colors.end, a->max_len);
         }
         if (a->controller_id != 0) {
-            ds_put_format(s, "%sid=%s%"PRIu16",",
+            ds_put_format(fp->s, "%sid=%s%"PRIu16",",
                           colors.param, colors.end, a->controller_id);
         }
         if (a->userdata_len) {
-            ds_put_format(s, "%suserdata=%s", colors.param, colors.end);
-            format_hex_arg(s, a->userdata, a->userdata_len);
-            ds_put_char(s, ',');
+            ds_put_format(fp->s, "%suserdata=%s", colors.param, colors.end);
+            format_hex_arg(fp->s, a->userdata, a->userdata_len);
+            ds_put_char(fp->s, ',');
         }
         if (a->pause) {
-            ds_put_format(s, "%spause%s,", colors.value, colors.end);
+            ds_put_format(fp->s, "%spause%s,", colors.value, colors.end);
         }
-        ds_chomp(s, ',');
-        ds_put_format(s, "%s)%s", colors.paren, colors.end);
+        ds_chomp(fp->s, ',');
+        ds_put_format(fp->s, "%s)%s", colors.paren, colors.end);
     }
 }
 
@@ -1074,10 +1061,7 @@  encode_ENQUEUE(const struct ofpact_enqueue *enqueue,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_ENQUEUE(char *arg,
-              const struct ofputil_port_map *port_map,
-              struct ofpbuf *ofpacts,
-              enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_ENQUEUE(char *arg, const struct ofpact_parse_params *pp)
 {
     char *sp = NULL;
     char *port = strtok_r(arg, ":q,", &sp);
@@ -1089,8 +1073,8 @@  parse_ENQUEUE(char *arg,
                        "\"enqueue(PORT,QUEUE)\"");
     }
 
-    enqueue = ofpact_put_ENQUEUE(ofpacts);
-    if (!ofputil_port_from_string(port, port_map, &enqueue->port)) {
+    enqueue = ofpact_put_ENQUEUE(pp->ofpacts);
+    if (!ofputil_port_from_string(port, pp->port_map, &enqueue->port)) {
         return xasprintf("%s: enqueue to unknown port", port);
     }
     return str_to_u32(queue, &enqueue->queue);
@@ -1098,11 +1082,11 @@  parse_ENQUEUE(char *arg,
 
 static void
 format_ENQUEUE(const struct ofpact_enqueue *a,
-               const struct ofputil_port_map *port_map, struct ds *s)
+               const struct ofpact_format_params *fp)
 {
-    ds_put_format(s, "%senqueue:%s", colors.param, colors.end);
-    ofputil_format_port(a->port, port_map, s);
-    ds_put_format(s, ":%"PRIu32, a->queue);
+    ds_put_format(fp->s, "%senqueue:%s", colors.param, colors.end);
+    ofputil_format_port(a->port, fp->port_map, fp->s);
+    ds_put_format(fp->s, ":%"PRIu32, a->queue);
 }
 
 /* Action structure for NXAST_OUTPUT_REG.
@@ -1245,21 +1229,17 @@  encode_OUTPUT_REG(const struct ofpact_output_reg *output_reg,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_OUTPUT_REG(const char *arg,
-                 const struct ofputil_port_map *port_map,
-                 struct ofpbuf *ofpacts,
-                 enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_OUTPUT_REG(const char *arg, const struct ofpact_parse_params *pp)
 {
-    return parse_OUTPUT(arg, port_map, ofpacts, usable_protocols);
+    return parse_OUTPUT(arg, pp);
 }
 
 static void
 format_OUTPUT_REG(const struct ofpact_output_reg *a,
-                  const struct ofputil_port_map *port_map OVS_UNUSED,
-                  struct ds *s)
+                  const struct ofpact_format_params *fp)
 {
-    ds_put_format(s, "%soutput:%s", colors.special, colors.end);
-    mf_format_subfield(&a->src, s);
+    ds_put_format(fp->s, "%soutput:%s", colors.special, colors.end);
+    mf_format_subfield(&a->src, fp->s);
 }
 
 /* Action structure for NXAST_BUNDLE and NXAST_BUNDLE_LOAD.
@@ -1462,25 +1442,22 @@  encode_BUNDLE(const struct ofpact_bundle *bundle,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_BUNDLE(const char *arg, const struct ofputil_port_map *port_map,
-             struct ofpbuf *ofpacts,
-             enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_BUNDLE(const char *arg, const struct ofpact_parse_params *pp)
 {
-    return bundle_parse(arg, port_map, ofpacts);
+    return bundle_parse(arg, pp->port_map, pp->ofpacts);
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_bundle_load(const char *arg, const struct ofputil_port_map *port_map,
-                  struct ofpbuf *ofpacts)
+parse_bundle_load(const char *arg, const struct ofpact_parse_params *pp)
 {
-    return bundle_parse_load(arg, port_map, ofpacts);
+    return bundle_parse_load(arg, pp->port_map, pp->ofpacts);
 }
 
 static void
 format_BUNDLE(const struct ofpact_bundle *a,
-              const struct ofputil_port_map *port_map, struct ds *s)
+              const struct ofpact_format_params *fp)
 {
-    bundle_format(a, port_map, s);
+    bundle_format(a, fp->port_map, fp->s);
 }
 
 /* Set VLAN actions. */
@@ -1538,8 +1515,8 @@  encode_SET_VLAN_VID(const struct ofpact_vlan_vid *vlan_vid,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_set_vlan_vid(char *arg,
-                   struct ofpbuf *ofpacts, bool push_vlan_if_needed)
+parse_set_vlan_vid(char *arg, bool push_vlan_if_needed,
+                   const struct ofpact_parse_params *pp)
 {
     struct ofpact_vlan_vid *vlan_vid;
     uint16_t vid;
@@ -1553,27 +1530,23 @@  parse_set_vlan_vid(char *arg,
     if (vid & ~VLAN_VID_MASK) {
         return xasprintf("%s: not a valid VLAN VID", arg);
     }
-    vlan_vid = ofpact_put_SET_VLAN_VID(ofpacts);
+    vlan_vid = ofpact_put_SET_VLAN_VID(pp->ofpacts);
     vlan_vid->vlan_vid = vid;
     vlan_vid->push_vlan_if_needed = push_vlan_if_needed;
     return NULL;
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_SET_VLAN_VID(char *arg,
-                   const struct ofputil_port_map *port_map OVS_UNUSED,
-                   struct ofpbuf *ofpacts,
-                   enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_SET_VLAN_VID(char *arg, const struct ofpact_parse_params *pp)
 {
-    return parse_set_vlan_vid(arg, ofpacts, false);
+    return parse_set_vlan_vid(arg, false, pp);
 }
 
 static void
 format_SET_VLAN_VID(const struct ofpact_vlan_vid *a,
-                    const struct ofputil_port_map *port_map OVS_UNUSED,
-                    struct ds *s)
+                    const struct ofpact_format_params *fp)
 {
-    ds_put_format(s, "%s%s:%s%"PRIu16, colors.param,
+    ds_put_format(fp->s, "%s%s:%s%"PRIu16, colors.param,
                   a->push_vlan_if_needed ? "mod_vlan_vid" : "set_vlan_vid",
                   colors.end, a->vlan_vid);
 }
@@ -1633,8 +1606,8 @@  encode_SET_VLAN_PCP(const struct ofpact_vlan_pcp *vlan_pcp,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_set_vlan_pcp(char *arg,
-                   struct ofpbuf *ofpacts, bool push_vlan_if_needed)
+parse_set_vlan_pcp(char *arg, bool push_vlan_if_needed,
+                   const struct ofpact_parse_params *pp)
 {
     struct ofpact_vlan_pcp *vlan_pcp;
     uint8_t pcp;
@@ -1648,27 +1621,23 @@  parse_set_vlan_pcp(char *arg,
     if (pcp & ~7) {
         return xasprintf("%s: not a valid VLAN PCP", arg);
     }
-    vlan_pcp = ofpact_put_SET_VLAN_PCP(ofpacts);
+    vlan_pcp = ofpact_put_SET_VLAN_PCP(pp->ofpacts);
     vlan_pcp->vlan_pcp = pcp;
     vlan_pcp->push_vlan_if_needed = push_vlan_if_needed;
     return NULL;
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_SET_VLAN_PCP(char *arg,
-                   const struct ofputil_port_map *port_map OVS_UNUSED,
-                   struct ofpbuf *ofpacts,
-                   enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_SET_VLAN_PCP(char *arg, const struct ofpact_parse_params *pp)
 {
-    return parse_set_vlan_pcp(arg, ofpacts, false);
+    return parse_set_vlan_pcp(arg, false, pp);
 }
 
 static void
 format_SET_VLAN_PCP(const struct ofpact_vlan_pcp *a,
-                    const struct ofputil_port_map *port_map OVS_UNUSED,
-                    struct ds *s)
+                    const struct ofpact_format_params *fp)
 {
-    ds_put_format(s, "%s%s:%s%"PRIu8, colors.param,
+    ds_put_format(fp->s, "%s%s:%s%"PRIu8, colors.param,
                   a->push_vlan_if_needed ? "mod_vlan_pcp" : "set_vlan_pcp",
                   colors.end, a->vlan_pcp);
 }
@@ -1701,28 +1670,24 @@  encode_STRIP_VLAN(const struct ofpact_null *null OVS_UNUSED,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_STRIP_VLAN(char *arg OVS_UNUSED,
-                 const struct ofputil_port_map *port_map OVS_UNUSED,
-                 struct ofpbuf *ofpacts,
-                 enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_STRIP_VLAN(char *arg OVS_UNUSED, const struct ofpact_parse_params *pp)
 {
-    ofpact_put_STRIP_VLAN(ofpacts)->ofpact.raw = OFPAT_RAW10_STRIP_VLAN;
+    ofpact_put_STRIP_VLAN(pp->ofpacts)->ofpact.raw = OFPAT_RAW10_STRIP_VLAN;
     return NULL;
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_pop_vlan(struct ofpbuf *ofpacts)
+parse_pop_vlan(const struct ofpact_parse_params *pp)
 {
-    ofpact_put_STRIP_VLAN(ofpacts)->ofpact.raw = OFPAT_RAW11_POP_VLAN;
+    ofpact_put_STRIP_VLAN(pp->ofpacts)->ofpact.raw = OFPAT_RAW11_POP_VLAN;
     return NULL;
 }
 
 static void
 format_STRIP_VLAN(const struct ofpact_null *a,
-                  const struct ofputil_port_map *port_map OVS_UNUSED,
-                  struct ds *s)
+                  const struct ofpact_format_params *fp)
 {
-    ds_put_format(s, (a->ofpact.raw == OFPAT_RAW11_POP_VLAN
+    ds_put_format(fp->s, (a->ofpact.raw == OFPAT_RAW11_POP_VLAN
                     ? "%spop_vlan%s"
                     : "%sstrip_vlan%s"),
                   colors.value, colors.end);
@@ -1757,16 +1722,13 @@  encode_PUSH_VLAN(const struct ofpact_push_vlan *push_vlan,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_PUSH_VLAN(char *arg,
-                const struct ofputil_port_map *port_map OVS_UNUSED,
-                struct ofpbuf *ofpacts,
-                enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_PUSH_VLAN(char *arg, const struct ofpact_parse_params *pp)
 {
     struct ofpact_push_vlan *push_vlan;
     uint16_t ethertype;
     char *error;
 
-    *usable_protocols &= OFPUTIL_P_OF11_UP;
+    *pp->usable_protocols &= OFPUTIL_P_OF11_UP;
     error = str_to_u16(arg, "ethertype", &ethertype);
     if (error) {
         return error;
@@ -1775,17 +1737,16 @@  parse_PUSH_VLAN(char *arg,
     if (!eth_type_vlan(htons(ethertype))) {
         return xasprintf("%s: not a valid VLAN ethertype", arg);
     }
-    push_vlan = ofpact_put_PUSH_VLAN(ofpacts);
+    push_vlan = ofpact_put_PUSH_VLAN(pp->ofpacts);
     push_vlan->ethertype = htons(ethertype);
     return NULL;
 }
 
 static void
 format_PUSH_VLAN(const struct ofpact_push_vlan *push_vlan,
-                 const struct ofputil_port_map *port_map OVS_UNUSED,
-                 struct ds *s)
+                 const struct ofpact_format_params *fp)
 {
-    ds_put_format(s, "%spush_vlan:%s%#"PRIx16,
+    ds_put_format(fp->s, "%spush_vlan:%s%#"PRIx16,
                   colors.param, colors.end, ntohs(push_vlan->ethertype));
 }
 
@@ -1851,38 +1812,30 @@  encode_SET_ETH_DST(const struct ofpact_mac *mac,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_SET_ETH_SRC(char *arg,
-                  const struct ofputil_port_map *port_map OVS_UNUSED,
-                  struct ofpbuf *ofpacts,
-                  enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_SET_ETH_SRC(char *arg, const struct ofpact_parse_params *pp)
 {
-    return str_to_mac(arg, &ofpact_put_SET_ETH_SRC(ofpacts)->mac);
+    return str_to_mac(arg, &ofpact_put_SET_ETH_SRC(pp->ofpacts)->mac);
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_SET_ETH_DST(char *arg,
-                  const struct ofputil_port_map *port_map OVS_UNUSED,
-                  struct ofpbuf *ofpacts,
-                  enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_SET_ETH_DST(char *arg, const struct ofpact_parse_params *pp)
 {
-    return str_to_mac(arg, &ofpact_put_SET_ETH_DST(ofpacts)->mac);
+    return str_to_mac(arg, &ofpact_put_SET_ETH_DST(pp->ofpacts)->mac);
 }
 
 static void
 format_SET_ETH_SRC(const struct ofpact_mac *a,
-                   const struct ofputil_port_map *port_map OVS_UNUSED,
-                   struct ds *s)
+                   const struct ofpact_format_params *fp)
 {
-    ds_put_format(s, "%smod_dl_src:%s"ETH_ADDR_FMT,
+    ds_put_format(fp->s, "%smod_dl_src:%s"ETH_ADDR_FMT,
                   colors.param, colors.end, ETH_ADDR_ARGS(a->mac));
 }
 
 static void
 format_SET_ETH_DST(const struct ofpact_mac *a,
-                   const struct ofputil_port_map *port_map OVS_UNUSED,
-                   struct ds *s)
+                   const struct ofpact_format_params *fp)
 {
-    ds_put_format(s, "%smod_dl_dst:%s"ETH_ADDR_FMT,
+    ds_put_format(fp->s, "%smod_dl_dst:%s"ETH_ADDR_FMT,
                   colors.param, colors.end, ETH_ADDR_ARGS(a->mac));
 }
 
@@ -1937,38 +1890,30 @@  encode_SET_IPV4_DST(const struct ofpact_ipv4 *ipv4,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_SET_IPV4_SRC(char *arg,
-                   const struct ofputil_port_map *port_map OVS_UNUSED,
-                   struct ofpbuf *ofpacts,
-                   enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_SET_IPV4_SRC(char *arg, const struct ofpact_parse_params *pp)
 {
-    return str_to_ip(arg, &ofpact_put_SET_IPV4_SRC(ofpacts)->ipv4);
+    return str_to_ip(arg, &ofpact_put_SET_IPV4_SRC(pp->ofpacts)->ipv4);
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_SET_IPV4_DST(char *arg,
-                   const struct ofputil_port_map *port_map OVS_UNUSED,
-                   struct ofpbuf *ofpacts,
-                   enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_SET_IPV4_DST(char *arg, const struct ofpact_parse_params *pp)
 {
-    return str_to_ip(arg, &ofpact_put_SET_IPV4_DST(ofpacts)->ipv4);
+    return str_to_ip(arg, &ofpact_put_SET_IPV4_DST(pp->ofpacts)->ipv4);
 }
 
 static void
 format_SET_IPV4_SRC(const struct ofpact_ipv4 *a,
-                    const struct ofputil_port_map *port_map OVS_UNUSED,
-                    struct ds *s)
+                    const struct ofpact_format_params *fp)
 {
-    ds_put_format(s, "%smod_nw_src:%s"IP_FMT,
+    ds_put_format(fp->s, "%smod_nw_src:%s"IP_FMT,
                   colors.param, colors.end, IP_ARGS(a->ipv4));
 }
 
 static void
 format_SET_IPV4_DST(const struct ofpact_ipv4 *a,
-                    const struct ofputil_port_map *port_map OVS_UNUSED,
-                    struct ds *s)
+                    const struct ofpact_format_params *fp)
 {
-    ds_put_format(s, "%smod_nw_dst:%s"IP_FMT,
+    ds_put_format(fp->s, "%smod_nw_dst:%s"IP_FMT,
                   colors.param, colors.end, IP_ARGS(a->ipv4));
 }
 
@@ -1999,10 +1944,8 @@  encode_SET_IP_DSCP(const struct ofpact_dscp *dscp,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_SET_IP_DSCP(char *arg,
-                  const struct ofputil_port_map *port_map OVS_UNUSED,
-                  struct ofpbuf *ofpacts,
-                  enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_SET_IP_DSCP(char *arg, const struct ofpact_parse_params *pp)
+
 {
     uint8_t tos;
     char *error;
@@ -2015,16 +1958,16 @@  parse_SET_IP_DSCP(char *arg,
     if (tos & ~IP_DSCP_MASK) {
         return xasprintf("%s: not a valid TOS", arg);
     }
-    ofpact_put_SET_IP_DSCP(ofpacts)->dscp = tos;
+    ofpact_put_SET_IP_DSCP(pp->ofpacts)->dscp = tos;
     return NULL;
 }
 
 static void
 format_SET_IP_DSCP(const struct ofpact_dscp *a,
-                   const struct ofputil_port_map *port_map OVS_UNUSED,
-                   struct ds *s)
+                   const struct ofpact_format_params *fp)
 {
-    ds_put_format(s, "%smod_nw_tos:%s%d", colors.param, colors.end, a->dscp);
+    ds_put_format(fp->s, "%smod_nw_tos:%s%d",
+                  colors.param, colors.end, a->dscp);
 }
 
 /* Set IPv4/v6 ECN actions. */
@@ -2059,10 +2002,7 @@  encode_SET_IP_ECN(const struct ofpact_ecn *ip_ecn,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_SET_IP_ECN(char *arg,
-                 const struct ofputil_port_map *port_map OVS_UNUSED,
-                 struct ofpbuf *ofpacts,
-                 enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_SET_IP_ECN(char *arg, const struct ofpact_parse_params *pp)
 {
     uint8_t ecn;
     char *error;
@@ -2075,16 +2015,15 @@  parse_SET_IP_ECN(char *arg,
     if (ecn & ~IP_ECN_MASK) {
         return xasprintf("%s: not a valid ECN", arg);
     }
-    ofpact_put_SET_IP_ECN(ofpacts)->ecn = ecn;
+    ofpact_put_SET_IP_ECN(pp->ofpacts)->ecn = ecn;
     return NULL;
 }
 
 static void
 format_SET_IP_ECN(const struct ofpact_ecn *a,
-                  const struct ofputil_port_map *port_map OVS_UNUSED,
-                  struct ds *s)
+                  const struct ofpact_format_params *fp)
 {
-    ds_put_format(s, "%smod_nw_ecn:%s%d",
+    ds_put_format(fp->s, "%smod_nw_ecn:%s%d",
                   colors.param, colors.end, a->ecn);
 }
 
@@ -2113,10 +2052,8 @@  encode_SET_IP_TTL(const struct ofpact_ip_ttl *ttl,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_SET_IP_TTL(char *arg,
-                 const struct ofputil_port_map *port_map OVS_UNUSED,
-                 struct ofpbuf *ofpacts,
-                 enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_SET_IP_TTL(char *arg, const struct ofpact_parse_params *pp)
+
 {
     uint8_t ttl;
     char *error;
@@ -2126,16 +2063,16 @@  parse_SET_IP_TTL(char *arg,
         return error;
     }
 
-    ofpact_put_SET_IP_TTL(ofpacts)->ttl = ttl;
+    ofpact_put_SET_IP_TTL(pp->ofpacts)->ttl = ttl;
     return NULL;
 }
 
 static void
 format_SET_IP_TTL(const struct ofpact_ip_ttl *a,
-                  const struct ofputil_port_map *port_map OVS_UNUSED,
-                  struct ds *s)
+                  const struct ofpact_format_params *fp)
 {
-    ds_put_format(s, "%smod_nw_ttl:%s%d", colors.param, colors.end, a->ttl);
+    ds_put_format(fp->s, "%smod_nw_ttl:%s%d",
+                  colors.param, colors.end, a->ttl);
 }
 
 /* Set TCP/UDP/SCTP port actions. */
@@ -2200,39 +2137,33 @@  encode_SET_L4_DST_PORT(const struct ofpact_l4_port *l4_port,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_SET_L4_SRC_PORT(char *arg,
-                      const struct ofputil_port_map *port_map OVS_UNUSED,
-                      struct ofpbuf *ofpacts,
-                      enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_SET_L4_SRC_PORT(char *arg, const struct ofpact_parse_params *pp)
 {
     return str_to_u16(arg, "source port",
-                      &ofpact_put_SET_L4_SRC_PORT(ofpacts)->port);
+                      &ofpact_put_SET_L4_SRC_PORT(pp->ofpacts)->port);
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_SET_L4_DST_PORT(char *arg,
-                      const struct ofputil_port_map *port_map OVS_UNUSED,
-                      struct ofpbuf *ofpacts,
-                      enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_SET_L4_DST_PORT(char *arg, const struct ofpact_parse_params *pp)
 {
     return str_to_u16(arg, "destination port",
-                      &ofpact_put_SET_L4_DST_PORT(ofpacts)->port);
+                      &ofpact_put_SET_L4_DST_PORT(pp->ofpacts)->port);
 }
 
 static void
 format_SET_L4_SRC_PORT(const struct ofpact_l4_port *a,
-                       const struct ofputil_port_map *port_map OVS_UNUSED,
-                       struct ds *s)
+                       const struct ofpact_format_params *fp)
 {
-    ds_put_format(s, "%smod_tp_src:%s%d", colors.param, colors.end, a->port);
+    ds_put_format(fp->s, "%smod_tp_src:%s%d",
+                  colors.param, colors.end, a->port);
 }
 
 static void
 format_SET_L4_DST_PORT(const struct ofpact_l4_port *a,
-                       const struct ofputil_port_map *port_map OVS_UNUSED,
-                       struct ds *s)
+                       const struct ofpact_format_params *fp)
 {
-    ds_put_format(s, "%smod_tp_dst:%s%d", colors.param, colors.end, a->port);
+    ds_put_format(fp->s, "%smod_tp_dst:%s%d",
+                  colors.param, colors.end, a->port);
 }
 
 /* Action structure for OFPAT_COPY_FIELD. */
@@ -2520,21 +2451,17 @@  encode_REG_MOVE(const struct ofpact_reg_move *move,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_REG_MOVE(const char *arg,
-               const struct ofputil_port_map *port_map OVS_UNUSED,
-               struct ofpbuf *ofpacts,
-               enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_REG_MOVE(const char *arg, const struct ofpact_parse_params *pp)
 {
-    struct ofpact_reg_move *move = ofpact_put_REG_MOVE(ofpacts);
+    struct ofpact_reg_move *move = ofpact_put_REG_MOVE(pp->ofpacts);
     return nxm_parse_reg_move(move, arg);
 }
 
 static void
 format_REG_MOVE(const struct ofpact_reg_move *a,
-                const struct ofputil_port_map *port_map OVS_UNUSED,
-                struct ds *s)
+                const struct ofpact_format_params *fp)
 {
-    nxm_format_reg_move(a, s);
+    nxm_format_reg_move(a, fp->s);
 }
 
 /* Action structure for OFPAT12_SET_FIELD. */
@@ -3027,14 +2954,12 @@  set_field_split_str(char *arg, char **key, char **value, char **delim)
 }
 
 /* Parses a "set_field" action with argument 'arg', appending the parsed
- * action to 'ofpacts'.
+ * action to 'pp->ofpacts'.
  *
  * Returns NULL if successful, otherwise a malloc()'d string describing the
  * error.  The caller is responsible for freeing the returned string. */
 static char * OVS_WARN_UNUSED_RESULT
-set_field_parse__(char *arg, const struct ofputil_port_map *port_map,
-                  struct ofpbuf *ofpacts,
-                  enum ofputil_protocol *usable_protocols)
+set_field_parse__(char *arg, const struct ofpact_parse_params *pp)
 {
     char *value;
     char *delim;
@@ -3057,7 +2982,7 @@  set_field_parse__(char *arg, const struct ofputil_port_map *port_map,
     }
 
     delim[0] = '\0';
-    error = mf_parse(mf, value, port_map, &sf_value, &sf_mask);
+    error = mf_parse(mf, value, pp->port_map, &sf_value, &sf_mask);
     if (error) {
         return error;
     }
@@ -3066,30 +2991,28 @@  set_field_parse__(char *arg, const struct ofputil_port_map *port_map,
         return xasprintf("%s is not a valid value for field %s", value, key);
     }
 
-    *usable_protocols &= mf->usable_protocols_exact;
+    *pp->usable_protocols &= mf->usable_protocols_exact;
 
-    ofpact_put_set_field(ofpacts, mf, &sf_value, &sf_mask);
+    ofpact_put_set_field(pp->ofpacts, mf, &sf_value, &sf_mask);
     return NULL;
 }
 
 /* Parses 'arg' as the argument to a "set_field" action, and appends such an
- * action to 'ofpacts'.
+ * action to 'pp->ofpacts'.
  *
  * Returns NULL if successful, otherwise a malloc()'d string describing the
  * error.  The caller is responsible for freeing the returned string. */
 static char * OVS_WARN_UNUSED_RESULT
-parse_SET_FIELD(const char *arg, const struct ofputil_port_map *port_map,
-                struct ofpbuf *ofpacts,
-                enum ofputil_protocol *usable_protocols)
+parse_SET_FIELD(const char *arg, const struct ofpact_parse_params *pp)
 {
     char *copy = xstrdup(arg);
-    char *error = set_field_parse__(copy, port_map, ofpacts, usable_protocols);
+    char *error = set_field_parse__(copy, pp);
     free(copy);
     return error;
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_reg_load(char *arg, struct ofpbuf *ofpacts)
+parse_reg_load(char *arg, const struct ofpact_parse_params *pp)
 {
     struct mf_subfield dst;
     char *key, *value_str;
@@ -3123,8 +3046,8 @@  parse_reg_load(char *arg, struct ofpbuf *ofpacts)
         return error;
     }
 
-    struct ofpact_set_field *sf = ofpact_put_reg_load(ofpacts, dst.field, NULL,
-                                                      NULL);
+    struct ofpact_set_field *sf = ofpact_put_reg_load(pp->ofpacts, dst.field,
+                                                      NULL, NULL);
 
     bitwise_copy(&value, dst.field->n_bytes, 0, sf->value,
                  dst.field->n_bytes, dst.ofs, dst.n_bits);
@@ -3135,8 +3058,7 @@  parse_reg_load(char *arg, struct ofpbuf *ofpacts)
 
 static void
 format_SET_FIELD(const struct ofpact_set_field *a,
-                 const struct ofputil_port_map *port_map,
-                 struct ds *s)
+                 const struct ofpact_format_params *fp)
 {
     if (a->ofpact.raw == NXAST_RAW_REG_LOAD) {
         struct mf_subfield dst;
@@ -3144,17 +3066,18 @@  format_SET_FIELD(const struct ofpact_set_field *a,
 
         dst.ofs = dst.n_bits = 0;
         while (next_load_segment(a, &dst, &value)) {
-            ds_put_format(s, "%sload:%s%#"PRIx64"%s->%s",
+            ds_put_format(fp->s, "%sload:%s%#"PRIx64"%s->%s",
                           colors.special, colors.end, value,
                           colors.special, colors.end);
-            mf_format_subfield(&dst, s);
-            ds_put_char(s, ',');
+            mf_format_subfield(&dst, fp->s);
+            ds_put_char(fp->s, ',');
         }
-        ds_chomp(s, ',');
+        ds_chomp(fp->s, ',');
     } else {
-        ds_put_format(s, "%sset_field:%s", colors.special, colors.end);
-        mf_format(a->field, a->value, ofpact_set_field_mask(a), port_map, s);
-        ds_put_format(s, "%s->%s%s",
+        ds_put_format(fp->s, "%sset_field:%s", colors.special, colors.end);
+        mf_format(a->field, a->value, ofpact_set_field_mask(a),
+                  fp->port_map, fp->s);
+        ds_put_format(fp->s, "%s->%s%s",
                       colors.special, colors.end, a->field->name);
     }
 }
@@ -3319,37 +3242,29 @@  encode_STACK_POP(const struct ofpact_stack *stack,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_STACK_PUSH(char *arg,
-                 const struct ofputil_port_map *port_map OVS_UNUSED,
-                 struct ofpbuf *ofpacts,
-                 enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_STACK_PUSH(char *arg, const struct ofpact_parse_params *pp)
 {
-    return nxm_parse_stack_action(ofpact_put_STACK_PUSH(ofpacts), arg);
+    return nxm_parse_stack_action(ofpact_put_STACK_PUSH(pp->ofpacts), arg);
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_STACK_POP(char *arg,
-                const struct ofputil_port_map *port_map OVS_UNUSED,
-                struct ofpbuf *ofpacts,
-                enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_STACK_POP(char *arg, const struct ofpact_parse_params *pp)
 {
-    return nxm_parse_stack_action(ofpact_put_STACK_POP(ofpacts), arg);
+    return nxm_parse_stack_action(ofpact_put_STACK_POP(pp->ofpacts), arg);
 }
 
 static void
 format_STACK_PUSH(const struct ofpact_stack *a,
-                  const struct ofputil_port_map *port_map OVS_UNUSED,
-                  struct ds *s)
+                  const struct ofpact_format_params *fp)
 {
-    nxm_format_stack_push(a, s);
+    nxm_format_stack_push(a, fp->s);
 }
 
 static void
 format_STACK_POP(const struct ofpact_stack *a,
-                 const struct ofputil_port_map *port_map OVS_UNUSED,
-                 struct ds *s)
+                 const struct ofpact_format_params *fp)
 {
-    nxm_format_stack_pop(a, s);
+    nxm_format_stack_pop(a, fp->s);
 }
 
 /* Action structure for NXAST_DEC_TTL_CNT_IDS.
@@ -3457,66 +3372,62 @@  encode_DEC_TTL(const struct ofpact_cnt_ids *dec_ttl,
 }
 
 static void
-parse_noargs_dec_ttl(struct ofpbuf *ofpacts)
+parse_noargs_dec_ttl(const struct ofpact_parse_params *pp)
 {
     struct ofpact_cnt_ids *ids;
     uint16_t id = 0;
 
-    ofpact_put_DEC_TTL(ofpacts);
-    ofpbuf_put(ofpacts, &id, sizeof id);
-    ids = ofpacts->header;
+    ofpact_put_DEC_TTL(pp->ofpacts);
+    ofpbuf_put(pp->ofpacts, &id, sizeof id);
+    ids = pp->ofpacts->header;
     ids->n_controllers++;
-    ofpact_finish_DEC_TTL(ofpacts, &ids);
+    ofpact_finish_DEC_TTL(pp->ofpacts, &ids);
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_DEC_TTL(char *arg,
-              const struct ofputil_port_map *port_map OVS_UNUSED,
-              struct ofpbuf *ofpacts,
-              enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_DEC_TTL(char *arg, const struct ofpact_parse_params *pp)
 {
     if (*arg == '\0') {
-        parse_noargs_dec_ttl(ofpacts);
+        parse_noargs_dec_ttl(pp);
     } else {
         struct ofpact_cnt_ids *ids;
         char *cntr;
 
-        ids = ofpact_put_DEC_TTL(ofpacts);
+        ids = ofpact_put_DEC_TTL(pp->ofpacts);
         ids->ofpact.raw = NXAST_RAW_DEC_TTL_CNT_IDS;
         for (cntr = strtok_r(arg, ", ", &arg); cntr != NULL;
              cntr = strtok_r(NULL, ", ", &arg)) {
             uint16_t id = atoi(cntr);
 
-            ofpbuf_put(ofpacts, &id, sizeof id);
-            ids = ofpacts->header;
+            ofpbuf_put(pp->ofpacts, &id, sizeof id);
+            ids = pp->ofpacts->header;
             ids->n_controllers++;
         }
         if (!ids->n_controllers) {
             return xstrdup("dec_ttl_cnt_ids: expected at least one controller "
                            "id.");
         }
-        ofpact_finish_DEC_TTL(ofpacts, &ids);
+        ofpact_finish_DEC_TTL(pp->ofpacts, &ids);
     }
     return NULL;
 }
 
 static void
 format_DEC_TTL(const struct ofpact_cnt_ids *a,
-               const struct ofputil_port_map *port_map OVS_UNUSED,
-               struct ds *s)
+               const struct ofpact_format_params *fp)
 {
     size_t i;
 
-    ds_put_format(s, "%sdec_ttl%s", colors.paren, colors.end);
+    ds_put_format(fp->s, "%sdec_ttl%s", colors.paren, colors.end);
     if (a->ofpact.raw == NXAST_RAW_DEC_TTL_CNT_IDS) {
-        ds_put_format(s, "%s(%s", colors.paren, colors.end);
+        ds_put_format(fp->s, "%s(%s", colors.paren, colors.end);
         for (i = 0; i < a->n_controllers; i++) {
             if (i) {
-                ds_put_cstr(s, ",");
+                ds_put_cstr(fp->s, ",");
             }
-            ds_put_format(s, "%"PRIu16, a->cnt_ids[i]);
+            ds_put_format(fp->s, "%"PRIu16, a->cnt_ids[i]);
         }
-        ds_put_format(s, "%s)%s", colors.paren, colors.end);
+        ds_put_format(fp->s, "%s)%s", colors.paren, colors.end);
     }
 }
 
@@ -3544,12 +3455,10 @@  encode_SET_MPLS_LABEL(const struct ofpact_mpls_label *label,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_SET_MPLS_LABEL(char *arg,
-                     const struct ofputil_port_map *port_map OVS_UNUSED,
-                     struct ofpbuf *ofpacts,
-                     enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_SET_MPLS_LABEL(char *arg, const struct ofpact_parse_params *pp)
 {
-    struct ofpact_mpls_label *mpls_label = ofpact_put_SET_MPLS_LABEL(ofpacts);
+    struct ofpact_mpls_label *mpls_label
+        = ofpact_put_SET_MPLS_LABEL(pp->ofpacts);
     if (*arg == '\0') {
         return xstrdup("set_mpls_label: expected label.");
     }
@@ -3560,10 +3469,9 @@  parse_SET_MPLS_LABEL(char *arg,
 
 static void
 format_SET_MPLS_LABEL(const struct ofpact_mpls_label *a,
-                      const struct ofputil_port_map *port_map OVS_UNUSED,
-                      struct ds *s)
+                      const struct ofpact_format_params *fp)
 {
-    ds_put_format(s, "%sset_mpls_label(%s%"PRIu32"%s)%s",
+    ds_put_format(fp->s, "%sset_mpls_label(%s%"PRIu32"%s)%s",
                   colors.paren, colors.end, ntohl(a->label),
                   colors.paren, colors.end);
 }
@@ -3591,12 +3499,9 @@  encode_SET_MPLS_TC(const struct ofpact_mpls_tc *tc,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_SET_MPLS_TC(char *arg,
-                  const struct ofputil_port_map *port_map OVS_UNUSED,
-                  struct ofpbuf *ofpacts,
-                  enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_SET_MPLS_TC(char *arg, const struct ofpact_parse_params *pp)
 {
-    struct ofpact_mpls_tc *mpls_tc = ofpact_put_SET_MPLS_TC(ofpacts);
+    struct ofpact_mpls_tc *mpls_tc = ofpact_put_SET_MPLS_TC(pp->ofpacts);
 
     if (*arg == '\0') {
         return xstrdup("set_mpls_tc: expected tc.");
@@ -3608,10 +3513,9 @@  parse_SET_MPLS_TC(char *arg,
 
 static void
 format_SET_MPLS_TC(const struct ofpact_mpls_tc *a,
-                   const struct ofputil_port_map *port_map OVS_UNUSED,
-                   struct ds *s)
+                   const struct ofpact_format_params *fp)
 {
-    ds_put_format(s, "%sset_mpls_ttl(%s%"PRIu8"%s)%s",
+    ds_put_format(fp->s, "%sset_mpls_ttl(%s%"PRIu8"%s)%s",
                   colors.paren, colors.end, a->tc,
                   colors.paren, colors.end);
 }
@@ -3635,17 +3539,14 @@  encode_SET_MPLS_TTL(const struct ofpact_mpls_ttl *ttl,
 }
 
 /* Parses 'arg' as the argument to a "set_mpls_ttl" action, and appends such an
- * action to 'ofpacts'.
+ * action to 'pp->ofpacts'.
  *
  * Returns NULL if successful, otherwise a malloc()'d string describing the
  * error.  The caller is responsible for freeing the returned string. */
 static char * OVS_WARN_UNUSED_RESULT
-parse_SET_MPLS_TTL(char *arg,
-                   const struct ofputil_port_map *port_map OVS_UNUSED,
-                   struct ofpbuf *ofpacts,
-                   enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_SET_MPLS_TTL(char *arg, const struct ofpact_parse_params *pp)
 {
-    struct ofpact_mpls_ttl *mpls_ttl = ofpact_put_SET_MPLS_TTL(ofpacts);
+    struct ofpact_mpls_ttl *mpls_ttl = ofpact_put_SET_MPLS_TTL(pp->ofpacts);
 
     if (*arg == '\0') {
         return xstrdup("set_mpls_ttl: expected ttl.");
@@ -3657,10 +3558,9 @@  parse_SET_MPLS_TTL(char *arg,
 
 static void
 format_SET_MPLS_TTL(const struct ofpact_mpls_ttl *a,
-                    const struct ofputil_port_map *port_map OVS_UNUSED,
-                    struct ds *s)
+                    const struct ofpact_format_params *fp)
 {
-    ds_put_format(s, "%sset_mpls_ttl(%s%"PRIu8"%s)%s",
+    ds_put_format(fp->s, "%sset_mpls_ttl(%s%"PRIu8"%s)%s",
                   colors.paren, colors.end, a->ttl,
                   colors.paren, colors.end);
 }
@@ -3682,21 +3582,17 @@  encode_DEC_MPLS_TTL(const struct ofpact_null *null OVS_UNUSED,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_DEC_MPLS_TTL(char *arg OVS_UNUSED,
-                   const struct ofputil_port_map *port_map OVS_UNUSED,
-                   struct ofpbuf *ofpacts,
-                   enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_DEC_MPLS_TTL(char *arg OVS_UNUSED, const struct ofpact_parse_params *pp)
 {
-    ofpact_put_DEC_MPLS_TTL(ofpacts);
+    ofpact_put_DEC_MPLS_TTL(pp->ofpacts);
     return NULL;
 }
 
 static void
 format_DEC_MPLS_TTL(const struct ofpact_null *a OVS_UNUSED,
-                    const struct ofputil_port_map *port_map OVS_UNUSED,
-                    struct ds *s)
+                    const struct ofpact_format_params *fp)
 {
-    ds_put_format(s, "%sdec_mpls_ttl%s", colors.value, colors.end);
+    ds_put_format(fp->s, "%sdec_mpls_ttl%s", colors.value, colors.end);
 }
 
 /* Push MPLS label action. */
@@ -3725,27 +3621,23 @@  encode_PUSH_MPLS(const struct ofpact_push_mpls *push_mpls,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_PUSH_MPLS(char *arg,
-                const struct ofputil_port_map *port_map OVS_UNUSED,
-                struct ofpbuf *ofpacts,
-                enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_PUSH_MPLS(char *arg, const struct ofpact_parse_params *pp)
 {
     uint16_t ethertype;
     char *error;
 
     error = str_to_u16(arg, "push_mpls", &ethertype);
     if (!error) {
-        ofpact_put_PUSH_MPLS(ofpacts)->ethertype = htons(ethertype);
+        ofpact_put_PUSH_MPLS(pp->ofpacts)->ethertype = htons(ethertype);
     }
     return error;
 }
 
 static void
 format_PUSH_MPLS(const struct ofpact_push_mpls *a,
-                 const struct ofputil_port_map *port_map OVS_UNUSED,
-                 struct ds *s)
+                 const struct ofpact_format_params *fp)
 {
-    ds_put_format(s, "%spush_mpls:%s0x%04"PRIx16,
+    ds_put_format(fp->s, "%spush_mpls:%s0x%04"PRIx16,
                   colors.param, colors.end, ntohs(a->ethertype));
 }
 
@@ -3768,27 +3660,23 @@  encode_POP_MPLS(const struct ofpact_pop_mpls *pop_mpls,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_POP_MPLS(char *arg,
-               const struct ofputil_port_map *port_map OVS_UNUSED,
-               struct ofpbuf *ofpacts,
-               enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_POP_MPLS(char *arg, const struct ofpact_parse_params *pp)
 {
     uint16_t ethertype;
     char *error;
 
     error = str_to_u16(arg, "pop_mpls", &ethertype);
     if (!error) {
-        ofpact_put_POP_MPLS(ofpacts)->ethertype = htons(ethertype);
+        ofpact_put_POP_MPLS(pp->ofpacts)->ethertype = htons(ethertype);
     }
     return error;
 }
 
 static void
 format_POP_MPLS(const struct ofpact_pop_mpls *a,
-                const struct ofputil_port_map *port_map OVS_UNUSED,
-                struct ds *s)
+                const struct ofpact_format_params *fp)
 {
-    ds_put_format(s, "%spop_mpls:%s0x%04"PRIx16,
+    ds_put_format(fp->s, "%spop_mpls:%s0x%04"PRIx16,
                   colors.param, colors.end, ntohs(a->ethertype));
 }
 
@@ -3835,31 +3723,27 @@  encode_SET_TUNNEL(const struct ofpact_tunnel *tunnel,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_set_tunnel(char *arg, struct ofpbuf *ofpacts,
-                 enum ofp_raw_action_type raw)
+parse_set_tunnel(char *arg, enum ofp_raw_action_type raw,
+                 const struct ofpact_parse_params *pp)
 {
     struct ofpact_tunnel *tunnel;
 
-    tunnel = ofpact_put_SET_TUNNEL(ofpacts);
+    tunnel = ofpact_put_SET_TUNNEL(pp->ofpacts);
     tunnel->ofpact.raw = raw;
     return str_to_u64(arg, &tunnel->tun_id);
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_SET_TUNNEL(char *arg,
-                 const struct ofputil_port_map *port_map OVS_UNUSED,
-                 struct ofpbuf *ofpacts,
-                 enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_SET_TUNNEL(char *arg, const struct ofpact_parse_params *pp)
 {
-    return parse_set_tunnel(arg, ofpacts, NXAST_RAW_SET_TUNNEL);
+    return parse_set_tunnel(arg, NXAST_RAW_SET_TUNNEL, pp);
 }
 
 static void
 format_SET_TUNNEL(const struct ofpact_tunnel *a,
-                  const struct ofputil_port_map *port_map OVS_UNUSED,
-                  struct ds *s)
+                  const struct ofpact_format_params *fp)
 {
-    ds_put_format(s, "%sset_tunnel%s:%s%#"PRIx64, colors.param,
+    ds_put_format(fp->s, "%sset_tunnel%s:%s%#"PRIx64, colors.param,
                   (a->tun_id > UINT32_MAX
                    || a->ofpact.raw == NXAST_RAW_SET_TUNNEL64 ? "64" : ""),
                   colors.end, a->tun_id);
@@ -3884,20 +3768,16 @@  encode_SET_QUEUE(const struct ofpact_queue *queue,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_SET_QUEUE(char *arg,
-                const struct ofputil_port_map *port_map OVS_UNUSED,
-                struct ofpbuf *ofpacts,
-                enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_SET_QUEUE(char *arg, const struct ofpact_parse_params *pp)
 {
-    return str_to_u32(arg, &ofpact_put_SET_QUEUE(ofpacts)->queue_id);
+    return str_to_u32(arg, &ofpact_put_SET_QUEUE(pp->ofpacts)->queue_id);
 }
 
 static void
 format_SET_QUEUE(const struct ofpact_queue *a,
-                 const struct ofputil_port_map *port_map OVS_UNUSED,
-                 struct ds *s)
+                 const struct ofpact_format_params *fp)
 {
-    ds_put_format(s, "%sset_queue:%s%"PRIu32,
+    ds_put_format(fp->s, "%sset_queue:%s%"PRIu32,
                   colors.param, colors.end, a->queue_id);
 }
 
@@ -3919,20 +3799,17 @@  encode_POP_QUEUE(const struct ofpact_null *null OVS_UNUSED,
 
 static char * OVS_WARN_UNUSED_RESULT
 parse_POP_QUEUE(const char *arg OVS_UNUSED,
-                const struct ofputil_port_map *port_map OVS_UNUSED,
-                struct ofpbuf *ofpacts,
-                enum ofputil_protocol *usable_protocols OVS_UNUSED)
+                const struct ofpact_parse_params *pp)
 {
-    ofpact_put_POP_QUEUE(ofpacts);
+    ofpact_put_POP_QUEUE(pp->ofpacts);
     return NULL;
 }
 
 static void
 format_POP_QUEUE(const struct ofpact_null *a OVS_UNUSED,
-                 const struct ofputil_port_map *port_map OVS_UNUSED,
-                 struct ds *s)
+                 const struct ofpact_format_params *fp)
 {
-    ds_put_format(s, "%spop_queue%s", colors.value, colors.end);
+    ds_put_format(fp->s, "%spop_queue%s", colors.value, colors.end);
 }
 
 /* Action structure for NXAST_FIN_TIMEOUT.
@@ -3993,12 +3870,9 @@  encode_FIN_TIMEOUT(const struct ofpact_fin_timeout *fin_timeout,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_FIN_TIMEOUT(char *arg,
-                  const struct ofputil_port_map *port_map OVS_UNUSED,
-                  struct ofpbuf *ofpacts,
-                  enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_FIN_TIMEOUT(char *arg, const struct ofpact_parse_params *pp)
 {
-    struct ofpact_fin_timeout *oft = ofpact_put_FIN_TIMEOUT(ofpacts);
+    struct ofpact_fin_timeout *oft = ofpact_put_FIN_TIMEOUT(pp->ofpacts);
     char *key, *value;
 
     while (ofputil_parse_key_value(&arg, &key, &value)) {
@@ -4022,20 +3896,19 @@  parse_FIN_TIMEOUT(char *arg,
 
 static void
 format_FIN_TIMEOUT(const struct ofpact_fin_timeout *a,
-                   const struct ofputil_port_map *port_map OVS_UNUSED,
-                   struct ds *s)
+                   const struct ofpact_format_params *fp)
 {
-    ds_put_format(s, "%sfin_timeout(%s", colors.paren, colors.end);
+    ds_put_format(fp->s, "%sfin_timeout(%s", colors.paren, colors.end);
     if (a->fin_idle_timeout) {
-        ds_put_format(s, "%sidle_timeout=%s%"PRIu16",",
+        ds_put_format(fp->s, "%sidle_timeout=%s%"PRIu16",",
                       colors.param, colors.end, a->fin_idle_timeout);
     }
     if (a->fin_hard_timeout) {
-        ds_put_format(s, "%shard_timeout=%s%"PRIu16",",
+        ds_put_format(fp->s, "%shard_timeout=%s%"PRIu16",",
                       colors.param, colors.end, a->fin_hard_timeout);
     }
-    ds_chomp(s, ',');
-    ds_put_format(s, "%s)%s", colors.paren, colors.end);
+    ds_chomp(fp->s, ',');
+    ds_put_format(fp->s, "%s)%s", colors.paren, colors.end);
 }
 
 /* Action structure for NXAST_ENCAP */
@@ -4149,10 +4022,7 @@  parse_ed_props(const uint16_t prop_class, char **arg, int *n_props, struct ofpbu
  */
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_ENCAP(char *arg,
-            const struct ofputil_port_map *port_map OVS_UNUSED,
-            struct ofpbuf *out,
-            enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_ENCAP(char *arg, const struct ofpact_parse_params *pp)
 {
     struct ofpact_encap *encap;
     char *key, *value, *str;
@@ -4160,7 +4030,7 @@  parse_ENCAP(char *arg,
     uint16_t prop_class;
     int n_props = 0;
 
-    encap = ofpact_put_ENCAP(out);
+    encap = ofpact_put_ENCAP(pp->ofpacts);
     encap->hdr_size = 0;
     /* Parse encap header type. */
     str = arg;
@@ -4174,14 +4044,14 @@  parse_ENCAP(char *arg,
         return xasprintf("Invalid encap prop class: %s", key);
     }
     /* Parse encap properties. */
-    error = parse_ed_props(prop_class, &value, &n_props, out);
+    error = parse_ed_props(prop_class, &value, &n_props, pp->ofpacts);
     if (error != NULL) {
         return error;
     }
-    /* ofbuf out may have been re-allocated. */
-    encap = out->header;
+    /* ofpbuf may have been re-allocated. */
+    encap = pp->ofpacts->header;
     encap->n_props = n_props;
-    ofpact_finish_ENCAP(out, &encap);
+    ofpact_finish_ENCAP(pp->ofpacts, &encap);
     return NULL;
 }
 
@@ -4221,17 +4091,16 @@  format_ed_props(struct ds *s, uint16_t n_props,
 
 static void
 format_ENCAP(const struct ofpact_encap *a,
-             const struct ofputil_port_map *port_map OVS_UNUSED,
-             struct ds *s)
+             const struct ofpact_format_params *fp)
 {
-    ds_put_format(s, "%sencap(%s", colors.paren, colors.end);
-    ds_put_format(s, "%s", format_encap_pkt_type(a->new_pkt_type));
+    ds_put_format(fp->s, "%sencap(%s", colors.paren, colors.end);
+    ds_put_format(fp->s, "%s", format_encap_pkt_type(a->new_pkt_type));
     if (a->n_props > 0) {
-        ds_put_format(s, "%s(%s", colors.paren, colors.end);
-        format_ed_props(s, a->n_props, a->props);
-        ds_put_format(s, "%s)%s", colors.paren, colors.end);
+        ds_put_format(fp->s, "%s(%s", colors.paren, colors.end);
+        format_ed_props(fp->s, a->n_props, a->props);
+        ds_put_format(fp->s, "%s)%s", colors.paren, colors.end);
     }
-    ds_put_format(s, "%s)%s", colors.paren, colors.end);
+    ds_put_format(fp->s, "%s)%s", colors.paren, colors.end);
 }
 
 /* Action structure for NXAST_DECAP */
@@ -4281,17 +4150,14 @@  encode_DECAP(const struct ofpact_decap *decap,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_DECAP(char *arg,
-            const struct ofputil_port_map *port_map OVS_UNUSED,
-            struct ofpbuf *ofpacts,
-            enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_DECAP(char *arg, const struct ofpact_parse_params *pp)
 {
     struct ofpact_decap *decap;
     char *key, *value, *pos;
     char *error = NULL;
     uint16_t ns, type;
 
-    decap = ofpact_put_DECAP(ofpacts);
+    decap = ofpact_put_DECAP(pp->ofpacts);
     /* Default next packet_type is PT_USE_NEXT_PROTO. */
     decap->new_pkt_type = htonl(PT_USE_NEXT_PROTO);
 
@@ -4328,16 +4194,15 @@  parse_DECAP(char *arg,
 
 static void
 format_DECAP(const struct ofpact_decap *a,
-             const struct ofputil_port_map *port_map OVS_UNUSED,
-             struct ds *s)
+             const struct ofpact_format_params *fp)
 {
-    ds_put_format(s, "%sdecap(%s", colors.paren, colors.end);
+    ds_put_format(fp->s, "%sdecap(%s", colors.paren, colors.end);
     if (a->new_pkt_type != htonl(PT_USE_NEXT_PROTO)) {
-        ds_put_format(s, "packet_type(ns=%"PRIu16",id=%#"PRIx16")",
+        ds_put_format(fp->s, "packet_type(ns=%"PRIu16",id=%#"PRIx16")",
                       pt_ns(a->new_pkt_type),
                       pt_ns_type(a->new_pkt_type));
     }
-    ds_put_format(s, "%s)%s", colors.paren, colors.end);
+    ds_put_format(fp->s, "%s)%s", colors.paren, colors.end);
 }
 
 /* Action dec_nsh_ttl */
@@ -4357,20 +4222,17 @@  encode_DEC_NSH_TTL(const struct ofpact_null *null OVS_UNUSED,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_DEC_NSH_TTL(char *arg OVS_UNUSED,
-           const struct ofputil_port_map *port_map OVS_UNUSED,
-           struct ofpbuf *ofpacts,
-           enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_DEC_NSH_TTL(char *arg OVS_UNUSED, const struct ofpact_parse_params *pp)
 {
-    ofpact_put_DEC_NSH_TTL(ofpacts);
+    ofpact_put_DEC_NSH_TTL(pp->ofpacts);
     return NULL;
 }
 
 static void
 format_DEC_NSH_TTL(const struct ofpact_null *a OVS_UNUSED,
-            const struct ofputil_port_map *port_map OVS_UNUSED, struct ds *s)
+                   const struct ofpact_format_params *fp)
 {
-    ds_put_format(s, "%sdec_nsh_ttl%s", colors.special, colors.end);
+    ds_put_format(fp->s, "%sdec_nsh_ttl%s", colors.special, colors.end);
 }
 
 
@@ -4517,19 +4379,16 @@  encode_RESUBMIT(const struct ofpact_resubmit *resubmit,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_RESUBMIT(char *arg,
-               const struct ofputil_port_map *port_map,
-               struct ofpbuf *ofpacts,
-               enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_RESUBMIT(char *arg, const struct ofpact_parse_params *pp)
 {
     struct ofpact_resubmit *resubmit;
     char *in_port_s, *table_s, *ct_s;
 
-    resubmit = ofpact_put_RESUBMIT(ofpacts);
+    resubmit = ofpact_put_RESUBMIT(pp->ofpacts);
 
     in_port_s = strsep(&arg, ",");
     if (in_port_s && in_port_s[0]) {
-        if (!ofputil_port_from_string(in_port_s, port_map,
+        if (!ofputil_port_from_string(in_port_s, pp->port_map,
                                       &resubmit->in_port)) {
             return xasprintf("%s: resubmit to unknown port", in_port_s);
         }
@@ -4570,24 +4429,24 @@  parse_RESUBMIT(char *arg,
 
 static void
 format_RESUBMIT(const struct ofpact_resubmit *a,
-                const struct ofputil_port_map *port_map, struct ds *s)
+                const struct ofpact_format_params *fp)
 {
     if (a->in_port != OFPP_IN_PORT && a->table_id == 255) {
-        ds_put_format(s, "%sresubmit:%s", colors.special, colors.end);
-        ofputil_format_port(a->in_port, port_map, s);
+        ds_put_format(fp->s, "%sresubmit:%s", colors.special, colors.end);
+        ofputil_format_port(a->in_port, fp->port_map, fp->s);
     } else {
-        ds_put_format(s, "%sresubmit(%s", colors.paren, colors.end);
+        ds_put_format(fp->s, "%sresubmit(%s", colors.paren, colors.end);
         if (a->in_port != OFPP_IN_PORT) {
-            ofputil_format_port(a->in_port, port_map, s);
+            ofputil_format_port(a->in_port, fp->port_map, fp->s);
         }
-        ds_put_char(s, ',');
+        ds_put_char(fp->s, ',');
         if (a->table_id != 255) {
-            ds_put_format(s, "%"PRIu8, a->table_id);
+            ds_put_format(fp->s, "%"PRIu8, a->table_id);
         }
         if (a->with_ct_orig) {
-            ds_put_cstr(s, ",ct");
+            ds_put_cstr(fp->s, ",ct");
         }
-        ds_put_format(s, "%s)%s", colors.paren, colors.end);
+        ds_put_format(fp->s, "%s)%s", colors.paren, colors.end);
     }
 }
 
@@ -5163,18 +5022,16 @@  encode_LEARN(const struct ofpact_learn *learn,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_LEARN(char *arg, const struct ofputil_port_map *port_map,
-            struct ofpbuf *ofpacts,
-            enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_LEARN(char *arg, const struct ofpact_parse_params *pp)
 {
-    return learn_parse(arg, port_map, ofpacts);
+    return learn_parse(arg, pp->port_map, pp->ofpacts);
 }
 
 static void
 format_LEARN(const struct ofpact_learn *a,
-             const struct ofputil_port_map *port_map, struct ds *s)
+             const struct ofpact_format_params *fp)
 {
-    learn_format(a, port_map, s);
+    learn_format(a, fp->port_map, fp->s);
 }
 
 /* Action structure for NXAST_CONJUNCTION. */
@@ -5227,20 +5084,16 @@  encode_CONJUNCTION(const struct ofpact_conjunction *oc,
 
 static void
 format_CONJUNCTION(const struct ofpact_conjunction *oc,
-                   const struct ofputil_port_map *port_map OVS_UNUSED,
-                   struct ds *s)
+                   const struct ofpact_format_params *fp)
 {
-    ds_put_format(s, "%sconjunction(%s%"PRIu32",%d/%"PRIu8"%s)%s",
+    ds_put_format(fp->s, "%sconjunction(%s%"PRIu32",%d/%"PRIu8"%s)%s",
                   colors.paren, colors.end,
                   oc->id, oc->clause + 1, oc->n_clauses,
                   colors.paren, colors.end);
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_CONJUNCTION(const char *arg,
-                  const struct ofputil_port_map *port_map OVS_UNUSED,
-                  struct ofpbuf *ofpacts,
-                  enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_CONJUNCTION(const char *arg, const struct ofpact_parse_params *pp)
 {
     uint8_t n_clauses;
     uint8_t clause;
@@ -5263,7 +5116,7 @@  parse_CONJUNCTION(const char *arg,
                        "number of clauses");
     }
 
-    add_conjunction(ofpacts, id, clause - 1, n_clauses);
+    add_conjunction(pp->ofpacts, id, clause - 1, n_clauses);
     return NULL;
 }
 
@@ -5382,20 +5235,16 @@  encode_MULTIPATH(const struct ofpact_multipath *mp,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_MULTIPATH(const char *arg,
-                const struct ofputil_port_map *port_map OVS_UNUSED,
-                struct ofpbuf *ofpacts,
-                enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_MULTIPATH(const char *arg, const struct ofpact_parse_params *pp)
 {
-    return multipath_parse(ofpact_put_MULTIPATH(ofpacts), arg);
+    return multipath_parse(ofpact_put_MULTIPATH(pp->ofpacts), arg);
 }
 
 static void
 format_MULTIPATH(const struct ofpact_multipath *a,
-                 const struct ofputil_port_map *port_map OVS_UNUSED,
-                 struct ds *s)
+                 const struct ofpact_format_params *fp)
 {
-    multipath_format(a, s);
+    multipath_format(a, fp->s);
 }
 
 /* Action structure for NXAST_NOTE.
@@ -5449,30 +5298,27 @@  encode_NOTE(const struct ofpact_note *note,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_NOTE(const char *arg,
-           const struct ofputil_port_map *port_map OVS_UNUSED,
-           struct ofpbuf *ofpacts,
-           enum ofputil_protocol *usable_protocols OVS_UNUSED)
-{
-    size_t start_ofs = ofpacts->size;
-    ofpact_put_NOTE(ofpacts);
-    arg = ofpbuf_put_hex(ofpacts, arg, NULL);
+parse_NOTE(const char *arg, const struct ofpact_parse_params *pp)
+{
+    size_t start_ofs = pp->ofpacts->size;
+    ofpact_put_NOTE(pp->ofpacts);
+    arg = ofpbuf_put_hex(pp->ofpacts, arg, NULL);
     if (arg[0]) {
         return xstrdup("bad hex digit in `note' argument");
     }
-    struct ofpact_note *note = ofpbuf_at_assert(ofpacts, start_ofs,
+    struct ofpact_note *note = ofpbuf_at_assert(pp->ofpacts, start_ofs,
                                                 sizeof *note);
-    note->length = ofpacts->size - (start_ofs + sizeof *note);
-    ofpact_finish_NOTE(ofpacts, &note);
+    note->length = pp->ofpacts->size - (start_ofs + sizeof *note);
+    ofpact_finish_NOTE(pp->ofpacts, &note);
     return NULL;
 }
 
 static void
 format_NOTE(const struct ofpact_note *a,
-            const struct ofputil_port_map *port_map OVS_UNUSED, struct ds *s)
+            const struct ofpact_format_params *fp)
 {
-    ds_put_format(s, "%snote:%s", colors.param, colors.end);
-    format_hex_arg(s, a->data, a->length);
+    ds_put_format(fp->s, "%snote:%s", colors.param, colors.end);
+    format_hex_arg(fp->s, a->data, a->length);
 }
 
 /* Exit action. */
@@ -5492,20 +5338,17 @@  encode_EXIT(const struct ofpact_null *null OVS_UNUSED,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_EXIT(char *arg OVS_UNUSED,
-           const struct ofputil_port_map *port_map OVS_UNUSED,
-           struct ofpbuf *ofpacts,
-           enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_EXIT(char *arg OVS_UNUSED, const struct ofpact_parse_params *pp)
 {
-    ofpact_put_EXIT(ofpacts);
+    ofpact_put_EXIT(pp->ofpacts);
     return NULL;
 }
 
 static void
 format_EXIT(const struct ofpact_null *a OVS_UNUSED,
-            const struct ofputil_port_map *port_map OVS_UNUSED, struct ds *s)
+            const struct ofpact_format_params *fp)
 {
-    ds_put_format(s, "%sexit%s", colors.special, colors.end);
+    ds_put_format(fp->s, "%sexit%s", colors.special, colors.end);
 }
 
 /* Unroll xlate action. */
@@ -5520,19 +5363,16 @@  encode_UNROLL_XLATE(const struct ofpact_unroll_xlate *unroll OVS_UNUSED,
 
 static char * OVS_WARN_UNUSED_RESULT
 parse_UNROLL_XLATE(char *arg OVS_UNUSED,
-                   const struct ofputil_port_map *port_map OVS_UNUSED,
-                   struct ofpbuf *ofpacts OVS_UNUSED,
-                   enum ofputil_protocol *usable_protocols OVS_UNUSED)
+                   const struct ofpact_parse_params *pp OVS_UNUSED)
 {
     OVS_NOT_REACHED();
 }
 
 static void
 format_UNROLL_XLATE(const struct ofpact_unroll_xlate *a,
-                    const struct ofputil_port_map *port_map OVS_UNUSED,
-                    struct ds *s)
+                    const struct ofpact_format_params *fp)
 {
-    ds_put_format(s, "%sunroll_xlate(%s%stable=%s%"PRIu8
+    ds_put_format(fp->s, "%sunroll_xlate(%s%stable=%s%"PRIu8
                   ", %scookie=%s%"PRIu64"%s)%s",
                   colors.paren,   colors.end,
                   colors.special, colors.end, a->rule_table_id,
@@ -5587,33 +5427,30 @@  encode_CLONE(const struct ofpact_nest *clone,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_CLONE(char *arg, const struct ofputil_port_map *port_map,
-            struct ofpbuf *ofpacts,
-            enum ofputil_protocol *usable_protocols)
+parse_CLONE(char *arg, const struct ofpact_parse_params *pp)
 {
-    const size_t clone_offset = ofpacts_pull(ofpacts);
-    struct ofpact_nest *clone = ofpact_put_CLONE(ofpacts);
+    const size_t clone_offset = ofpacts_pull(pp->ofpacts);
+    struct ofpact_nest *clone = ofpact_put_CLONE(pp->ofpacts);
     char *error;
 
-    ofpbuf_pull(ofpacts, sizeof *clone);
-    error = ofpacts_parse_copy(arg, port_map, ofpacts,
-                               usable_protocols, false, 0);
+    ofpbuf_pull(pp->ofpacts, sizeof *clone);
+    error = ofpacts_parse_copy(arg, pp, false, 0);
     /* header points to the action list */
-    ofpacts->header = ofpbuf_push_uninit(ofpacts, sizeof *clone);
-    clone = ofpacts->header;
+    pp->ofpacts->header = ofpbuf_push_uninit(pp->ofpacts, sizeof *clone);
+    clone = pp->ofpacts->header;
 
-    ofpact_finish_CLONE(ofpacts, &clone);
-    ofpbuf_push_uninit(ofpacts, clone_offset);
+    ofpact_finish_CLONE(pp->ofpacts, &clone);
+    ofpbuf_push_uninit(pp->ofpacts, clone_offset);
     return error;
 }
 
 static void
 format_CLONE(const struct ofpact_nest *a,
-             const struct ofputil_port_map *port_map, struct ds *s)
+             const struct ofpact_format_params *fp)
 {
-    ds_put_format(s, "%sclone(%s", colors.paren, colors.end);
-    ofpacts_format(a->actions, ofpact_nest_get_action_len(a), port_map, s);
-    ds_put_format(s, "%s)%s", colors.paren, colors.end);
+    ds_put_format(fp->s, "%sclone(%s", colors.paren, colors.end);
+    ofpacts_format(a->actions, ofpact_nest_get_action_len(a), fp);
+    ds_put_format(fp->s, "%s)%s", colors.paren, colors.end);
 }
 
 /* Action structure for NXAST_SAMPLE.
@@ -5766,16 +5603,14 @@  encode_SAMPLE(const struct ofpact_sample *sample,
 }
 
 /* Parses 'arg' as the argument to a "sample" action, and appends such an
- * action to 'ofpacts'.
+ * action to 'pp->ofpacts'.
  *
  * Returns NULL if successful, otherwise a malloc()'d string describing the
  * error.  The caller is responsible for freeing the returned string. */
 static char * OVS_WARN_UNUSED_RESULT
-parse_SAMPLE(char *arg, const struct ofputil_port_map *port_map,
-             struct ofpbuf *ofpacts,
-             enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_SAMPLE(char *arg, const struct ofpact_parse_params *pp)
 {
-    struct ofpact_sample *os = ofpact_put_SAMPLE(ofpacts);
+    struct ofpact_sample *os = ofpact_put_SAMPLE(pp->ofpacts);
     os->sampling_port = OFPP_NONE;
     os->direction = NX_ACTION_SAMPLE_DEFAULT;
 
@@ -5795,7 +5630,7 @@  parse_SAMPLE(char *arg, const struct ofputil_port_map *port_map,
         } else if (!strcmp(key, "obs_point_id")) {
             error = str_to_u32(value, &os->obs_point_id);
         } else if (!strcmp(key, "sampling_port")) {
-            if (!ofputil_port_from_string(value, port_map,
+            if (!ofputil_port_from_string(value, pp->port_map,
                                           &os->sampling_port)) {
                 error = xasprintf("%s: unknown port", value);
             }
@@ -5820,9 +5655,9 @@  parse_SAMPLE(char *arg, const struct ofputil_port_map *port_map,
 
 static void
 format_SAMPLE(const struct ofpact_sample *a,
-              const struct ofputil_port_map *port_map, struct ds *s)
+              const struct ofpact_format_params *fp)
 {
-    ds_put_format(s, "%ssample(%s%sprobability=%s%"PRIu16
+    ds_put_format(fp->s, "%ssample(%s%sprobability=%s%"PRIu16
                   ",%scollector_set_id=%s%"PRIu32
                   ",%sobs_domain_id=%s%"PRIu32
                   ",%sobs_point_id=%s%"PRIu32,
@@ -5832,15 +5667,15 @@  format_SAMPLE(const struct ofpact_sample *a,
                   colors.param, colors.end, a->obs_domain_id,
                   colors.param, colors.end, a->obs_point_id);
     if (a->sampling_port != OFPP_NONE) {
-        ds_put_format(s, ",%ssampling_port=%s", colors.param, colors.end);
-        ofputil_format_port(a->sampling_port, port_map, s);
+        ds_put_format(fp->s, ",%ssampling_port=%s", colors.param, colors.end);
+        ofputil_format_port(a->sampling_port, fp->port_map, fp->s);
     }
     if (a->direction == NX_ACTION_SAMPLE_INGRESS) {
-        ds_put_format(s, ",%singress%s", colors.param, colors.end);
+        ds_put_format(fp->s, ",%singress%s", colors.param, colors.end);
     } else if (a->direction == NX_ACTION_SAMPLE_EGRESS) {
-        ds_put_format(s, ",%segress%s", colors.param, colors.end);
+        ds_put_format(fp->s, ",%segress%s", colors.param, colors.end);
     }
-    ds_put_format(s, "%s)%s", colors.paren, colors.end);
+    ds_put_format(fp->s, "%s)%s", colors.paren, colors.end);
 }
 
 /* debug instructions. */
@@ -5873,21 +5708,17 @@  encode_DEBUG_RECIRC(const struct ofpact_null *n OVS_UNUSED,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_DEBUG_RECIRC(char *arg OVS_UNUSED,
-                   const struct ofputil_port_map *port_map OVS_UNUSED,
-                   struct ofpbuf *ofpacts,
-                   enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_DEBUG_RECIRC(char *arg OVS_UNUSED, const struct ofpact_parse_params *pp)
 {
-    ofpact_put_DEBUG_RECIRC(ofpacts);
+    ofpact_put_DEBUG_RECIRC(pp->ofpacts);
     return NULL;
 }
 
 static void
 format_DEBUG_RECIRC(const struct ofpact_null *a OVS_UNUSED,
-                    const struct ofputil_port_map *port_map OVS_UNUSED,
-                    struct ds *s)
+                    const struct ofpact_format_params *fp)
 {
-    ds_put_format(s, "%sdebug_recirc%s", colors.value, colors.end);
+    ds_put_format(fp->s, "%sdebug_recirc%s", colors.value, colors.end);
 }
 
 static enum ofperr
@@ -5910,21 +5741,17 @@  encode_DEBUG_SLOW(const struct ofpact_null *n OVS_UNUSED,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_DEBUG_SLOW(char *arg OVS_UNUSED,
-                 const struct ofputil_port_map *port_map OVS_UNUSED,
-                 struct ofpbuf *ofpacts,
-                 enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_DEBUG_SLOW(char *arg OVS_UNUSED, const struct ofpact_parse_params *pp)
 {
-    ofpact_put_DEBUG_SLOW(ofpacts);
+    ofpact_put_DEBUG_SLOW(pp->ofpacts);
     return NULL;
 }
 
 static void
 format_DEBUG_SLOW(const struct ofpact_null *a OVS_UNUSED,
-                  const struct ofputil_port_map *port_map OVS_UNUSED,
-                  struct ds *s)
+                  const struct ofpact_format_params *fp)
 {
-    ds_put_format(s, "%sdebug_slow%s", colors.value, colors.end);
+    ds_put_format(fp->s, "%sdebug_slow%s", colors.value, colors.end);
 }
 
 /* Action structure for NXAST_CT.
@@ -6187,25 +6014,22 @@  encode_CT(const struct ofpact_conntrack *conntrack,
 }
 
 static char *OVS_WARN_UNUSED_RESULT
-parse_NAT(char *arg, const struct ofputil_port_map *port_map OVS_UNUSED,
-          struct ofpbuf *, enum ofputil_protocol * OVS_UNUSED);
+parse_NAT(char *arg, const struct ofpact_parse_params *pp);
 
 /* Parses 'arg' as the argument to a "ct" action, and appends such an
- * action to 'ofpacts'.
+ * action to 'pp->ofpacts'.
  *
  * Returns NULL if successful, otherwise a malloc()'d string describing the
  * error.  The caller is responsible for freeing the returned string. */
 static char * OVS_WARN_UNUSED_RESULT
-parse_CT(char *arg, const struct ofputil_port_map *port_map,
-         struct ofpbuf *ofpacts,
-         enum ofputil_protocol *usable_protocols)
+parse_CT(char *arg, const struct ofpact_parse_params *pp)
 {
-    const size_t ct_offset = ofpacts_pull(ofpacts);
+    const size_t ct_offset = ofpacts_pull(pp->ofpacts);
     struct ofpact_conntrack *oc;
     char *error = NULL;
     char *key, *value;
 
-    oc = ofpact_put_CT(ofpacts);
+    oc = ofpact_put_CT(pp->ofpacts);
     oc->flags = 0;
     oc->recirc_table = NX_CT_RECIRC_NONE;
     while (ofputil_parse_key_value(&arg, &key, &value)) {
@@ -6231,26 +6055,27 @@  parse_CT(char *arg, const struct ofputil_port_map *port_map,
         } else if (!strcmp(key, "alg")) {
             error = str_to_connhelper(value, &oc->alg);
         } else if (!strcmp(key, "nat")) {
-            const size_t nat_offset = ofpacts_pull(ofpacts);
+            const size_t nat_offset = ofpacts_pull(pp->ofpacts);
 
-            error = parse_NAT(value, port_map, ofpacts, usable_protocols);
+            error = parse_NAT(value, pp);
             /* Update CT action pointer and length. */
-            ofpacts->header = ofpbuf_push_uninit(ofpacts, nat_offset);
-            oc = ofpacts->header;
+            pp->ofpacts->header = ofpbuf_push_uninit(pp->ofpacts, nat_offset);
+            oc = pp->ofpacts->header;
         } else if (!strcmp(key, "exec")) {
             /* Hide existing actions from ofpacts_parse_copy(), so the
              * nesting can be handled transparently. */
             enum ofputil_protocol usable_protocols2;
-            const size_t exec_offset = ofpacts_pull(ofpacts);
+            const size_t exec_offset = ofpacts_pull(pp->ofpacts);
 
             /* Initializes 'usable_protocol2', fold it back to
              * '*usable_protocols' afterwards, so that we do not lose
              * restrictions already in there. */
-            error = ofpacts_parse_copy(value, port_map, ofpacts,
-                                       &usable_protocols2, false, OFPACT_CT);
-            *usable_protocols &= usable_protocols2;
-            ofpacts->header = ofpbuf_push_uninit(ofpacts, exec_offset);
-            oc = ofpacts->header;
+            struct ofpact_parse_params pp2 = *pp;
+            pp2.usable_protocols = &usable_protocols2;
+            error = ofpacts_parse_copy(value, &pp2, false, OFPACT_CT);
+            *pp->usable_protocols &= usable_protocols2;
+            pp->ofpacts->header = ofpbuf_push_uninit(pp->ofpacts, exec_offset);
+            oc = pp->ofpacts->header;
         } else {
             error = xasprintf("invalid argument to \"ct\" action: `%s'", key);
         }
@@ -6261,8 +6086,8 @@  parse_CT(char *arg, const struct ofputil_port_map *port_map,
     if (!error && oc->flags & NX_CT_F_FORCE && !(oc->flags & NX_CT_F_COMMIT)) {
         error = xasprintf("\"force\" flag requires \"commit\" flag.");
     }
-    ofpact_finish_CT(ofpacts, &oc);
-    ofpbuf_push_uninit(ofpacts, ct_offset);
+    ofpact_finish_CT(pp->ofpacts, &oc);
+    ofpbuf_push_uninit(pp->ofpacts, ct_offset);
     return error;
 }
 
@@ -6286,31 +6111,29 @@  format_alg(int port, struct ds *s)
 }
 
 static void format_NAT(const struct ofpact_nat *,
-                       const struct ofputil_port_map *port_map,
-                       struct ds *ds);
+                       const struct ofpact_format_params *fp);
 
 static void
 format_CT(const struct ofpact_conntrack *a,
-          const struct ofputil_port_map *port_map,
-          struct ds *s)
+          const struct ofpact_format_params *fp)
 {
-    ds_put_format(s, "%sct(%s", colors.paren, colors.end);
+    ds_put_format(fp->s, "%sct(%s", colors.paren, colors.end);
     if (a->flags & NX_CT_F_COMMIT) {
-        ds_put_format(s, "%scommit%s,", colors.value, colors.end);
+        ds_put_format(fp->s, "%scommit%s,", colors.value, colors.end);
     }
     if (a->flags & NX_CT_F_FORCE) {
-        ds_put_format(s, "%sforce%s,", colors.value, colors.end);
+        ds_put_format(fp->s, "%sforce%s,", colors.value, colors.end);
     }
     if (a->recirc_table != NX_CT_RECIRC_NONE) {
-        ds_put_format(s, "%stable=%s%"PRIu8",",
+        ds_put_format(fp->s, "%stable=%s%"PRIu8",",
                       colors.special, colors.end, a->recirc_table);
     }
     if (a->zone_src.field) {
-        ds_put_format(s, "%szone=%s", colors.param, colors.end);
-        mf_format_subfield(&a->zone_src, s);
-        ds_put_char(s, ',');
+        ds_put_format(fp->s, "%szone=%s", colors.param, colors.end);
+        mf_format_subfield(&a->zone_src, fp->s);
+        ds_put_char(fp->s, ',');
     } else if (a->zone_imm) {
-        ds_put_format(s, "%szone=%s%"PRIu16",",
+        ds_put_format(fp->s, "%szone=%s%"PRIu16",",
                       colors.param, colors.end, a->zone_imm);
     }
     /* If the first action is a NAT action, format it outside of the 'exec'
@@ -6318,19 +6141,19 @@  format_CT(const struct ofpact_conntrack *a,
     const struct ofpact *action = a->actions;
     size_t actions_len = ofpact_ct_get_action_len(a);
     if (actions_len && action->type == OFPACT_NAT) {
-        format_NAT(ofpact_get_NAT(action), port_map, s);
-        ds_put_char(s, ',');
+        format_NAT(ofpact_get_NAT(action), fp);
+        ds_put_char(fp->s, ',');
         actions_len -= OFPACT_ALIGN(action->len);
         action = ofpact_next(action);
     }
     if (actions_len) {
-        ds_put_format(s, "%sexec(%s", colors.paren, colors.end);
-        ofpacts_format(action, actions_len, port_map, s);
-        ds_put_format(s, "%s),%s", colors.paren, colors.end);
+        ds_put_format(fp->s, "%sexec(%s", colors.paren, colors.end);
+        ofpacts_format(action, actions_len, fp);
+        ds_put_format(fp->s, "%s),%s", colors.paren, colors.end);
     }
-    format_alg(a->alg, s);
-    ds_chomp(s, ',');
-    ds_put_format(s, "%s)%s", colors.paren, colors.end);
+    format_alg(a->alg, fp->s);
+    ds_chomp(fp->s, ',');
+    ds_put_format(fp->s, "%s)%s", colors.paren, colors.end);
 }
 
 /* ct_clear action. */
@@ -6351,21 +6174,17 @@  encode_CT_CLEAR(const struct ofpact_null *null OVS_UNUSED,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_CT_CLEAR(char *arg OVS_UNUSED,
-               const struct ofputil_port_map *port_map OVS_UNUSED,
-               struct ofpbuf *ofpacts,
-               enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_CT_CLEAR(char *arg OVS_UNUSED, const struct ofpact_parse_params *pp)
 {
-    ofpact_put_CT_CLEAR(ofpacts);
+    ofpact_put_CT_CLEAR(pp->ofpacts);
     return NULL;
 }
 
 static void
 format_CT_CLEAR(const struct ofpact_null *a OVS_UNUSED,
-                const struct ofputil_port_map *port_map OVS_UNUSED,
-                struct ds *s)
+                const struct ofpact_format_params *fp)
 {
-    ds_put_format(s, "%sct_clear%s", colors.value, colors.end);
+    ds_put_format(fp->s, "%sct_clear%s", colors.value, colors.end);
 }
 
 /* NAT action. */
@@ -6547,64 +6366,62 @@  decode_NXAST_RAW_NAT(const struct nx_action_nat *nan,
 }
 
 static void
-format_NAT(const struct ofpact_nat *a,
-           const struct ofputil_port_map *port_map OVS_UNUSED,
-           struct ds *ds)
+format_NAT(const struct ofpact_nat *a, const struct ofpact_format_params *fp)
 {
-    ds_put_format(ds, "%snat%s", colors.paren, colors.end);
+    ds_put_format(fp->s, "%snat%s", colors.paren, colors.end);
 
     if (a->flags & (NX_NAT_F_SRC | NX_NAT_F_DST)) {
-        ds_put_format(ds, "%s(%s", colors.paren, colors.end);
-        ds_put_format(ds, a->flags & NX_NAT_F_SRC ? "%ssrc%s" : "%sdst%s",
+        ds_put_format(fp->s, "%s(%s", colors.paren, colors.end);
+        ds_put_format(fp->s, a->flags & NX_NAT_F_SRC ? "%ssrc%s" : "%sdst%s",
                       colors.param, colors.end);
 
         if (a->range_af != AF_UNSPEC) {
-            ds_put_format(ds, "%s=%s", colors.param, colors.end);
+            ds_put_format(fp->s, "%s=%s", colors.param, colors.end);
 
             if (a->range_af == AF_INET) {
-                ds_put_format(ds, IP_FMT, IP_ARGS(a->range.addr.ipv4.min));
+                ds_put_format(fp->s, IP_FMT, IP_ARGS(a->range.addr.ipv4.min));
 
                 if (a->range.addr.ipv4.max
                     && a->range.addr.ipv4.max != a->range.addr.ipv4.min) {
-                    ds_put_format(ds, "-"IP_FMT,
+                    ds_put_format(fp->s, "-"IP_FMT,
                                   IP_ARGS(a->range.addr.ipv4.max));
                 }
             } else if (a->range_af == AF_INET6) {
-                ipv6_format_addr_bracket(&a->range.addr.ipv6.min, ds,
+                ipv6_format_addr_bracket(&a->range.addr.ipv6.min, fp->s,
                                         a->range.proto.min);
 
                 if (!ipv6_mask_is_any(&a->range.addr.ipv6.max)
                     && memcmp(&a->range.addr.ipv6.max, &a->range.addr.ipv6.min,
                               sizeof(struct in6_addr)) != 0) {
-                    ds_put_char(ds, '-');
-                    ipv6_format_addr_bracket(&a->range.addr.ipv6.max, ds,
+                    ds_put_char(fp->s, '-');
+                    ipv6_format_addr_bracket(&a->range.addr.ipv6.max, fp->s,
                                             a->range.proto.min);
                 }
             }
             if (a->range.proto.min) {
-                ds_put_char(ds, ':');
-                ds_put_format(ds, "%"PRIu16, a->range.proto.min);
+                ds_put_char(fp->s, ':');
+                ds_put_format(fp->s, "%"PRIu16, a->range.proto.min);
 
                 if (a->range.proto.max
                     && a->range.proto.max != a->range.proto.min) {
-                    ds_put_format(ds, "-%"PRIu16, a->range.proto.max);
+                    ds_put_format(fp->s, "-%"PRIu16, a->range.proto.max);
                 }
             }
-            ds_put_char(ds, ',');
+            ds_put_char(fp->s, ',');
 
             if (a->flags & NX_NAT_F_PERSISTENT) {
-                ds_put_format(ds, "%spersistent%s,",
+                ds_put_format(fp->s, "%spersistent%s,",
                               colors.value, colors.end);
             }
             if (a->flags & NX_NAT_F_PROTO_HASH) {
-                ds_put_format(ds, "%shash%s,", colors.value, colors.end);
+                ds_put_format(fp->s, "%shash%s,", colors.value, colors.end);
             }
             if (a->flags & NX_NAT_F_PROTO_RANDOM) {
-                ds_put_format(ds, "%srandom%s,", colors.value, colors.end);
+                ds_put_format(fp->s, "%srandom%s,", colors.value, colors.end);
             }
         }
-        ds_chomp(ds, ',');
-        ds_put_format(ds, "%s)%s", colors.paren, colors.end);
+        ds_chomp(fp->s, ',');
+        ds_put_format(fp->s, "%s)%s", colors.paren, colors.end);
     }
 }
 
@@ -6668,17 +6485,14 @@  error:
 
 
 /* Parses 'arg' as the argument to a "nat" action, and appends such an
- * action to 'ofpacts'.
+ * action to 'pp->ofpacts'.
  *
  * Returns NULL if successful, otherwise a malloc()'d string describing the
  * error.  The caller is responsible for freeing the returned string. */
 static char * OVS_WARN_UNUSED_RESULT
-parse_NAT(char *arg,
-          const struct ofputil_port_map *port_map OVS_UNUSED,
-          struct ofpbuf *ofpacts,
-          enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_NAT(char *arg, const struct ofpact_parse_params *pp)
 {
-    struct ofpact_nat *on = ofpact_put_NAT(ofpacts);
+    struct ofpact_nat *on = ofpact_put_NAT(pp->ofpacts);
     char *key, *value;
 
     on->flags = 0;
@@ -6766,9 +6580,7 @@  encode_OUTPUT_TRUNC(const struct ofpact_output_trunc *output_trunc,
 
 static char * OVS_WARN_UNUSED_RESULT
 parse_OUTPUT_TRUNC(const char *arg,
-                   const struct ofputil_port_map *port_map OVS_UNUSED,
-                   struct ofpbuf *ofpacts OVS_UNUSED,
-                   enum ofputil_protocol *usable_protocols OVS_UNUSED)
+                   const struct ofpact_parse_params *pp OVS_UNUSED)
 {
     /* Disable output_trunc parsing.  Expose as output(port=N,max_len=M) and
      * reuse parse_OUTPUT to parse output_trunc action. */
@@ -6777,11 +6589,11 @@  parse_OUTPUT_TRUNC(const char *arg,
 
 static void
 format_OUTPUT_TRUNC(const struct ofpact_output_trunc *a,
-                    const struct ofputil_port_map *port_map, struct ds *s)
+                    const struct ofpact_format_params *fp)
 {
-    ds_put_format(s, "%soutput%s(port=", colors.special, colors.end);
-    ofputil_format_port(a->port, port_map, s);
-    ds_put_format(s, ",max_len=%"PRIu32")", a->max_len);
+    ds_put_format(fp->s, "%soutput%s(port=", colors.special, colors.end);
+    ofputil_format_port(a->port, fp->port_map, fp->s);
+    ds_put_format(fp->s, ",max_len=%"PRIu32")", a->max_len);
 }
 
 
@@ -6797,21 +6609,17 @@  encode_METER(const struct ofpact_meter *meter,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_METER(char *arg,
-            const struct ofputil_port_map *port_map OVS_UNUSED,
-            struct ofpbuf *ofpacts,
-            enum ofputil_protocol *usable_protocols)
+parse_METER(char *arg, const struct ofpact_parse_params *pp)
 {
-    *usable_protocols &= OFPUTIL_P_OF13_UP;
-    return str_to_u32(arg, &ofpact_put_METER(ofpacts)->meter_id);
+    *pp->usable_protocols &= OFPUTIL_P_OF13_UP;
+    return str_to_u32(arg, &ofpact_put_METER(pp->ofpacts)->meter_id);
 }
 
 static void
 format_METER(const struct ofpact_meter *a,
-             const struct ofputil_port_map *port_map OVS_UNUSED,
-             struct ds *s)
+             const struct ofpact_format_params *fp)
 {
-    ds_put_format(s, "%smeter:%s%"PRIu32,
+    ds_put_format(fp->s, "%smeter:%s%"PRIu32,
                   colors.param, colors.end, a->meter_id);
 }
 
@@ -6828,21 +6636,17 @@  encode_CLEAR_ACTIONS(const struct ofpact_null *null OVS_UNUSED,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_CLEAR_ACTIONS(char *arg OVS_UNUSED,
-                    const struct ofputil_port_map *port_map OVS_UNUSED,
-                    struct ofpbuf *ofpacts,
-                    enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_CLEAR_ACTIONS(char *arg OVS_UNUSED, const struct ofpact_parse_params *pp)
 {
-    ofpact_put_CLEAR_ACTIONS(ofpacts);
+    ofpact_put_CLEAR_ACTIONS(pp->ofpacts);
     return NULL;
 }
 
 static void
 format_CLEAR_ACTIONS(const struct ofpact_null *a OVS_UNUSED,
-                     const struct ofputil_port_map *port_map OVS_UNUSED,
-                     struct ds *s)
+                     const struct ofpact_format_params *fp)
 {
-    ds_put_format(s, "%sclear_actions%s", colors.value, colors.end);
+    ds_put_format(fp->s, "%sclear_actions%s", colors.value, colors.end);
 }
 
 /* Write-Actions instruction. */
@@ -6863,17 +6667,15 @@  encode_WRITE_ACTIONS(const struct ofpact_nest *actions,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_WRITE_ACTIONS(char *arg, const struct ofputil_port_map *port_map,
-                    struct ofpbuf *ofpacts,
-                    enum ofputil_protocol *usable_protocols)
+parse_WRITE_ACTIONS(char *arg, const struct ofpact_parse_params *pp)
 {
-    size_t ofs = ofpacts_pull(ofpacts);
+    size_t ofs = ofpacts_pull(pp->ofpacts);
     struct ofpact_nest *on;
     char *error;
 
     /* Add a Write-Actions instruction and then pull it off. */
-    ofpact_put(ofpacts, OFPACT_WRITE_ACTIONS, sizeof *on);
-    ofpbuf_pull(ofpacts, sizeof *on);
+    ofpact_put(pp->ofpacts, OFPACT_WRITE_ACTIONS, sizeof *on);
+    ofpbuf_pull(pp->ofpacts, sizeof *on);
 
     /* Parse nested actions.
      *
@@ -6882,26 +6684,25 @@  parse_WRITE_ACTIONS(char *arg, const struct ofputil_port_map *port_map,
      * that it doesn't actually include the nested actions.  That means that
      * ofpacts_parse() would reject them as being part of an Apply-Actions that
      * follows a Write-Actions, which is an invalid order.  */
-    error = ofpacts_parse(arg, port_map, ofpacts, usable_protocols, false,
-                          OFPACT_WRITE_ACTIONS);
+    error = ofpacts_parse(arg, pp, false, OFPACT_WRITE_ACTIONS);
 
     /* Put the Write-Actions back on and update its length. */
-    on = ofpbuf_push_uninit(ofpacts, sizeof *on);
-    on->ofpact.len = ofpacts->size;
+    on = ofpbuf_push_uninit(pp->ofpacts, sizeof *on);
+    on->ofpact.len = pp->ofpacts->size;
 
     /* Put any previous actions or instructions back on. */
-    ofpbuf_push_uninit(ofpacts, ofs);
+    ofpbuf_push_uninit(pp->ofpacts, ofs);
 
     return error;
 }
 
 static void
 format_WRITE_ACTIONS(const struct ofpact_nest *a,
-                     const struct ofputil_port_map *port_map, struct ds *s)
+                     const struct ofpact_format_params *fp)
 {
-    ds_put_format(s, "%swrite_actions(%s", colors.paren, colors.end);
-    ofpacts_format(a->actions, ofpact_nest_get_action_len(a), port_map, s);
-    ds_put_format(s, "%s)%s", colors.paren, colors.end);
+    ds_put_format(fp->s, "%swrite_actions(%s", colors.paren, colors.end);
+    ofpacts_format(a->actions, ofpact_nest_get_action_len(a), fp);
+    ds_put_format(fp->s, "%s)%s", colors.paren, colors.end);
 }
 
 /* Action structure for NXAST_WRITE_METADATA.
@@ -6956,17 +6757,14 @@  encode_WRITE_METADATA(const struct ofpact_metadata *metadata,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_WRITE_METADATA(char *arg,
-                     const struct ofputil_port_map *port_map OVS_UNUSED,
-                     struct ofpbuf *ofpacts,
-                     enum ofputil_protocol *usable_protocols)
+parse_WRITE_METADATA(char *arg, const struct ofpact_parse_params *pp)
 {
     struct ofpact_metadata *om;
     char *mask = strchr(arg, '/');
 
-    *usable_protocols &= OFPUTIL_P_NXM_OF11_UP;
+    *pp->usable_protocols &= OFPUTIL_P_NXM_OF11_UP;
 
-    om = ofpact_put_WRITE_METADATA(ofpacts);
+    om = ofpact_put_WRITE_METADATA(pp->ofpacts);
     if (mask) {
         char *error;
 
@@ -6984,13 +6782,12 @@  parse_WRITE_METADATA(char *arg,
 
 static void
 format_WRITE_METADATA(const struct ofpact_metadata *a,
-                      const struct ofputil_port_map *port_map OVS_UNUSED,
-                      struct ds *s)
+                      const struct ofpact_format_params *fp)
 {
-    ds_put_format(s, "%swrite_metadata:%s%#"PRIx64,
+    ds_put_format(fp->s, "%swrite_metadata:%s%#"PRIx64,
                   colors.param, colors.end, ntohll(a->metadata));
     if (a->mask != OVS_BE64_MAX) {
-        ds_put_format(s, "/%#"PRIx64, ntohll(a->mask));
+        ds_put_format(fp->s, "/%#"PRIx64, ntohll(a->mask));
     }
 }
 
@@ -7016,12 +6813,9 @@  encode_GOTO_TABLE(const struct ofpact_goto_table *goto_table,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-parse_GOTO_TABLE(char *arg,
-                 const struct ofputil_port_map *port_map OVS_UNUSED,
-                 struct ofpbuf *ofpacts,
-                 enum ofputil_protocol *usable_protocols OVS_UNUSED)
+parse_GOTO_TABLE(char *arg, const struct ofpact_parse_params *pp)
 {
-    struct ofpact_goto_table *ogt = ofpact_put_GOTO_TABLE(ofpacts);
+    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");
@@ -7031,10 +6825,9 @@  parse_GOTO_TABLE(char *arg,
 
 static void
 format_GOTO_TABLE(const struct ofpact_goto_table *a,
-                  const struct ofputil_port_map *port_map OVS_UNUSED,
-                  struct ds *s)
+                  const struct ofpact_format_params *fp)
 {
-    ds_put_format(s, "%sgoto_table:%s%"PRIu8,
+    ds_put_format(fp->s, "%sgoto_table:%s%"PRIu8,
                   colors.param, colors.end, a->table_id);
 }
 
@@ -8780,10 +8573,12 @@  ofpacts_equal_stringwise(const struct ofpact *a, size_t a_len,
                          const struct ofpact *b, size_t b_len)
 {
     struct ds a_s = DS_EMPTY_INITIALIZER;
-    struct ds b_s = DS_EMPTY_INITIALIZER;
+    struct ofpact_format_params a_fp = { .s = &a_s };
+    ofpacts_format(a, a_len, &a_fp);
 
-    ofpacts_format(a, a_len, NULL, &a_s);
-    ofpacts_format(b, b_len, NULL, &b_s);
+    struct ds b_s = DS_EMPTY_INITIALIZER;
+    struct ofpact_format_params b_fp = { .s = &b_s };
+    ofpacts_format(b, b_len, &b_fp);
 
     bool equal = !strcmp(ds_cstr(&a_s), ds_cstr(&b_s));
 
@@ -8821,13 +8616,12 @@  ofpacts_get_meter(const struct ofpact ofpacts[], size_t ofpacts_len)
 
 static void
 ofpact_format(const struct ofpact *a,
-              const struct ofputil_port_map *port_map, struct ds *s)
+              const struct ofpact_format_params *fp)
 {
     switch (a->type) {
 #define OFPACT(ENUM, STRUCT, MEMBER, NAME)                              \
         case OFPACT_##ENUM:                                             \
-            format_##ENUM(ALIGNED_CAST(const struct STRUCT *, a),       \
-                          port_map, s);                               \
+            format_##ENUM(ALIGNED_CAST(const struct STRUCT *, a), fp);  \
             break;
         OFPACTS
 #undef OFPACT
@@ -8837,23 +8631,23 @@  ofpact_format(const struct ofpact *a,
 }
 
 /* Appends a string representing the 'ofpacts_len' bytes of ofpacts in
- * 'ofpacts' to 'string'.  If 'port_map' is nonnull, uses it to translate
- * port numbers to names in output. */
+ * 'ofpacts' to 'fp->s'.  If 'port_map' is nonnull, uses it to translate port
+ * numbers to names in output. */
 void
 ofpacts_format(const struct ofpact *ofpacts, size_t ofpacts_len,
-               const struct ofputil_port_map *port_map, struct ds *string)
+               const struct ofpact_format_params *fp)
 {
     if (!ofpacts_len) {
-        ds_put_format(string, "%sdrop%s", colors.drop, colors.end);
+        ds_put_format(fp->s, "%sdrop%s", colors.drop, colors.end);
     } else {
         const struct ofpact *a;
 
         OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
             if (a != ofpacts) {
-                ds_put_char(string, ',');
+                ds_put_char(fp->s, ',');
             }
 
-            ofpact_format(a, port_map, string);
+            ofpact_format(a, fp);
         }
     }
 }
@@ -8907,13 +8701,12 @@  ofpact_finish(struct ofpbuf *ofpacts, struct ofpact *ofpact)
 
 static char * OVS_WARN_UNUSED_RESULT
 ofpact_parse(enum ofpact_type type, char *value,
-             const struct ofputil_port_map *port_map, struct ofpbuf *ofpacts,
-             enum ofputil_protocol *usable_protocols)
+             const struct ofpact_parse_params *pp)
 {
     switch (type) {
 #define OFPACT(ENUM, STRUCT, MEMBER, NAME)                              \
         case OFPACT_##ENUM:                                             \
-            return parse_##ENUM(value, port_map, ofpacts, usable_protocols);
+            return parse_##ENUM(value, pp);
         OFPACTS
 #undef OFPACT
     default:
@@ -8943,9 +8736,7 @@  ofpact_type_from_name(const char *name, enum ofpact_type *type)
  * If 'outer_action' is specified, indicates that the actions being parsed
  * are nested within another action of the type specified in 'outer_action'. */
 static char * OVS_WARN_UNUSED_RESULT
-ofpacts_parse__(char *str, const struct ofputil_port_map *port_map,
-                struct ofpbuf *ofpacts,
-                enum ofputil_protocol *usable_protocols,
+ofpacts_parse__(char *str, const struct ofpact_parse_params *pp,
                 bool allow_instructions, enum ofpact_type outer_action)
 {
     int prev_inst = -1;
@@ -8962,31 +8753,28 @@  ofpacts_parse__(char *str, const struct ofputil_port_map *port_map,
         ofp_port_t port;
 
         if (ofpact_type_from_name(key, &type)) {
-            error = ofpact_parse(type, value, port_map,
-                                 ofpacts, usable_protocols);
+            error = ofpact_parse(type, value, pp);
             inst = ovs_instruction_type_from_ofpact_type(type);
         } else if (!strcasecmp(key, "mod_vlan_vid")) {
-            error = parse_set_vlan_vid(value, ofpacts, true);
+            error = parse_set_vlan_vid(value, true, pp);
         } else if (!strcasecmp(key, "mod_vlan_pcp")) {
-            error = parse_set_vlan_pcp(value, ofpacts, true);
+            error = parse_set_vlan_pcp(value, true, pp);
         } else if (!strcasecmp(key, "set_nw_ttl")) {
-            error = parse_SET_IP_TTL(value, port_map,
-                                     ofpacts, usable_protocols);
+            error = parse_SET_IP_TTL(value, pp);
         } else if (!strcasecmp(key, "pop_vlan")) {
-            error = parse_pop_vlan(ofpacts);
+            error = parse_pop_vlan(pp);
         } else if (!strcasecmp(key, "set_tunnel64")) {
-            error = parse_set_tunnel(value, ofpacts,
-                                     NXAST_RAW_SET_TUNNEL64);
+            error = parse_set_tunnel(value, NXAST_RAW_SET_TUNNEL64, pp);
         } else if (!strcasecmp(key, "load")) {
-            error = parse_reg_load(value, ofpacts);
+            error = parse_reg_load(value, pp);
         } else if (!strcasecmp(key, "bundle_load")) {
-            error = parse_bundle_load(value, port_map, ofpacts);
+            error = parse_bundle_load(value, pp);
         } else if (!strcasecmp(key, "drop")) {
             drop = true;
         } else if (!strcasecmp(key, "apply_actions")) {
             return xstrdup("apply_actions is the default instruction");
-        } else if (ofputil_port_from_string(key, port_map, &port)) {
-            ofpact_put_OUTPUT(ofpacts)->port = port;
+        } else if (ofputil_port_from_string(key, pp->port_map, &port)) {
+            ofpact_put_OUTPUT(pp->ofpacts)->port = port;
         } else {
             return xasprintf("unknown action %s", key);
         }
@@ -9013,12 +8801,12 @@  ofpacts_parse__(char *str, const struct ofputil_port_map *port_map,
         prev_inst = inst;
     }
 
-    if (drop && ofpacts->size) {
+    if (drop && pp->ofpacts->size) {
         return xstrdup("\"drop\" must not be accompanied by any other action "
                        "or instruction");
     }
 
-    retval = ofpacts_verify(ofpacts->data, ofpacts->size,
+    retval = ofpacts_verify(pp->ofpacts->data, pp->ofpacts->size,
                             (allow_instructions
                              ? (1u << N_OVS_INSTRUCTIONS) - 1
                              : 1u << OVSINST_OFPIT11_APPLY_ACTIONS),
@@ -9031,32 +8819,27 @@  ofpacts_parse__(char *str, const struct ofputil_port_map *port_map,
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-ofpacts_parse(char *str, const struct ofputil_port_map *port_map,
-              struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols,
+ofpacts_parse(char *str, const struct ofpact_parse_params *pp,
               bool allow_instructions, enum ofpact_type outer_action)
 {
-    uint32_t orig_size = ofpacts->size;
-    char *error = ofpacts_parse__(str, port_map, ofpacts, usable_protocols,
-                                  allow_instructions, outer_action);
+    uint32_t orig_size = pp->ofpacts->size;
+    char *error = ofpacts_parse__(str, pp, allow_instructions, outer_action);
     if (error) {
-        ofpacts->size = orig_size;
+        pp->ofpacts->size = orig_size;
     }
     return error;
 }
 
 static char * OVS_WARN_UNUSED_RESULT
-ofpacts_parse_copy(const char *s_, const struct ofputil_port_map *port_map,
-                   struct ofpbuf *ofpacts,
-                   enum ofputil_protocol *usable_protocols,
+ofpacts_parse_copy(const char *s_, const struct ofpact_parse_params *pp,
                    bool allow_instructions, enum ofpact_type outer_action)
 {
     char *error, *s;
 
-    *usable_protocols = OFPUTIL_P_ANY;
+    *pp->usable_protocols = OFPUTIL_P_ANY;
 
     s = xstrdup(s_);
-    error = ofpacts_parse(s, port_map, ofpacts, usable_protocols,
-                          allow_instructions, outer_action);
+    error = ofpacts_parse(s, pp, allow_instructions, outer_action);
     free(s);
 
     return error;
@@ -9069,12 +8852,9 @@  ofpacts_parse_copy(const char *s_, const struct ofputil_port_map *port_map,
  * Returns NULL if successful, otherwise a malloc()'d string describing the
  * error.  The caller is responsible for freeing the returned string. */
 char * OVS_WARN_UNUSED_RESULT
-ofpacts_parse_actions(const char *s, const struct ofputil_port_map *port_map,
-                      struct ofpbuf *ofpacts,
-                      enum ofputil_protocol *usable_protocols)
+ofpacts_parse_actions(const char *s, const struct ofpact_parse_params *pp)
 {
-    return ofpacts_parse_copy(s, port_map, ofpacts, usable_protocols,
-                              false, 0);
+    return ofpacts_parse_copy(s, pp, false, 0);
 }
 
 /* Parses 's' as a set of OpenFlow instructions and appends the instructions to
@@ -9083,12 +8863,9 @@  ofpacts_parse_actions(const char *s, const struct ofputil_port_map *port_map,
  * Returns NULL if successful, otherwise a malloc()'d string describing the
  * error.  The caller is responsible for freeing the returned string. */
 char * OVS_WARN_UNUSED_RESULT
-ofpacts_parse_instructions(const char *s,
-                           const struct ofputil_port_map *port_map,
-                           struct ofpbuf *ofpacts,
-                           enum ofputil_protocol *usable_protocols)
+ofpacts_parse_instructions(const char *s, const struct ofpact_parse_params *pp)
 {
-    return ofpacts_parse_copy(s, port_map, ofpacts, usable_protocols, true, 0);
+    return ofpacts_parse_copy(s, pp, true, 0);
 }
 
 const char *
diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c
index bacbaa63886e..b68081740f09 100644
--- a/lib/ofp-parse.c
+++ b/lib/ofp-parse.c
@@ -557,8 +557,12 @@  parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string,
         char *error;
 
         ofpbuf_init(&ofpacts, 32);
-        error = ofpacts_parse_instructions(act_str, port_map, &ofpacts,
-                                           &action_usable_protocols);
+        struct ofpact_parse_params pp = {
+            .port_map = port_map,
+            .ofpacts = &ofpacts,
+            .usable_protocols = &action_usable_protocols
+        };
+        error = ofpacts_parse_instructions(act_str, &pp);
         *usable_protocols &= action_usable_protocols;
         if (!error) {
             enum ofperr err;
@@ -713,8 +717,12 @@  parse_ofp_packet_out_str__(struct ofputil_packet_out *po, char *string,
     }
 
     if (act_str) {
-        error = ofpacts_parse_actions(act_str, port_map, &ofpacts,
-                                      &action_usable_protocols);
+        struct ofpact_parse_params pp = {
+            .port_map = port_map,
+            .ofpacts = &ofpacts,
+            .usable_protocols = &action_usable_protocols,
+        };
+        error = ofpacts_parse_actions(act_str, &pp);
         *usable_protocols &= action_usable_protocols;
         if (error) {
             goto out;
@@ -1487,8 +1495,12 @@  parse_bucket_str(struct ofputil_bucket *bucket, char *str_,
     ds_chomp(&actions, ',');
 
     ofpbuf_init(&ofpacts, 0);
-    error = ofpacts_parse_actions(ds_cstr(&actions), port_map, &ofpacts,
-                                  usable_protocols);
+    struct ofpact_parse_params pp = {
+        .port_map = port_map,
+        .ofpacts = &ofpacts,
+        .usable_protocols = usable_protocols,
+    };
+    error = ofpacts_parse_actions(ds_cstr(&actions), &pp);
     ds_destroy(&actions);
     if (error) {
         ofpbuf_uninit(&ofpacts);
diff --git a/lib/ofp-print.c b/lib/ofp-print.c
index 8a1f426ade36..639d3bddf36d 100644
--- a/lib/ofp-print.c
+++ b/lib/ofp-print.c
@@ -205,15 +205,20 @@  ofp_print_packet_in(struct ds *string, const struct ofp_header *oh,
         ds_put_cstr(string, " continuation.conntracked=true\n");
     }
 
+    struct ofpact_format_params fp = {
+        .port_map = port_map,
+        .s = string,
+    };
+
     if (pin.actions_len) {
         ds_put_cstr(string, " continuation.actions=");
-        ofpacts_format(pin.actions, pin.actions_len, port_map, string);
+        ofpacts_format(pin.actions, pin.actions_len, &fp);
         ds_put_char(string, '\n');
     }
 
     if (pin.action_set_len) {
         ds_put_cstr(string, " continuation.action_set=");
-        ofpacts_format(pin.action_set, pin.action_set_len, port_map, string);
+        ofpacts_format(pin.action_set, pin.action_set_len, &fp);
         ds_put_char(string, '\n');
     }
 
@@ -252,7 +257,11 @@  ofp_print_packet_out(struct ds *string, const struct ofp_header *oh,
     match_format(&po.flow_metadata, port_map, string, OFP_DEFAULT_PRIORITY);
 
     ds_put_cstr(string, " actions=");
-    ofpacts_format(po.ofpacts, po.ofpacts_len, port_map, string);
+    struct ofpact_format_params fp = {
+        .port_map = port_map,
+        .s = string,
+    };
+    ofpacts_format(po.ofpacts, po.ofpacts_len, &fp);
 
     if (po.buffer_id == UINT32_MAX) {
         ds_put_format(string, " data_len=%"PRIuSIZE, po.packet_len);
@@ -916,7 +925,11 @@  ofp_print_flow_mod(struct ds *s, const struct ofp_header *oh,
     ofp_print_flow_flags(s, fm.flags);
 
     ds_put_cstr(s, "actions=");
-    ofpacts_format(fm.ofpacts, fm.ofpacts_len, port_map, s);
+    struct ofpact_format_params fp = {
+        .port_map = port_map,
+        .s = s,
+    };
+    ofpacts_format(fm.ofpacts, fm.ofpacts_len, &fp);
     ofpbuf_uninit(&ofpacts);
 
     return 0;
@@ -1749,7 +1762,11 @@  ofp_print_flow_stats(struct ds *string, const struct ofputil_flow_stats *fs,
     }
 
     ds_put_format(string, "%sactions=%s", colors.actions, colors.end);
-    ofpacts_format(fs->ofpacts, fs->ofpacts_len, port_map, string);
+    struct ofpact_format_params fp = {
+        .port_map = port_map,
+        .s = string,
+    };
+    ofpacts_format(fs->ofpacts, fs->ofpacts_len, &fp);
 }
 
 static enum ofperr
@@ -2544,8 +2561,11 @@  ofp_print_nxst_flow_monitor_reply(struct ds *string,
                 ds_put_char(string, ' ');
             }
             ds_put_cstr(string, "actions=");
-            ofpacts_format(update.ofpacts, update.ofpacts_len, port_map,
-                           string);
+            struct ofpact_format_params fp = {
+                .port_map = port_map,
+                .s = string,
+            };
+            ofpacts_format(update.ofpacts, update.ofpacts_len, &fp);
         }
     }
 }
@@ -2676,7 +2696,11 @@  ofp_print_group(struct ds *s, uint32_t group_id, uint8_t type,
         }
 
         ds_put_cstr(s, "actions=");
-        ofpacts_format(bucket->ofpacts, bucket->ofpacts_len, port_map, s);
+        struct ofpact_format_params fp = {
+            .port_map = port_map,
+            .s = s,
+        };
+        ofpacts_format(bucket->ofpacts, bucket->ofpacts_len, &fp);
         ds_put_char(s, ',');
     }
 
diff --git a/ofproto/ofproto-dpif-trace.c b/ofproto/ofproto-dpif-trace.c
index 4999d1d6f326..7b29013db99a 100644
--- a/ofproto/ofproto-dpif-trace.c
+++ b/ofproto/ofproto-dpif-trace.c
@@ -463,8 +463,12 @@  ofproto_unixctl_trace_actions(struct unixctl_conn *conn, int argc,
     ofpbuf_init(&ofpacts, 0);
 
     /* Parse actions. */
-    error = ofpacts_parse_actions(argv[--argc], NULL,
-                                  &ofpacts, &usable_protocols);
+    struct ofpact_parse_params pp = {
+        .port_map = NULL,
+        .ofpacts = &ofpacts,
+        .usable_protocols = &usable_protocols,
+    };
+    error = ofpacts_parse_actions(argv[--argc], &pp);
     if (error) {
         unixctl_command_reply_error(conn, error);
         free(error);
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index 896d5e290a46..fa9a89334163 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -693,7 +693,8 @@  xlate_report_actions(const struct xlate_ctx *ctx, enum oftrace_node_type type,
     if (OVS_UNLIKELY(ctx->xin->trace)) {
         struct ds s = DS_EMPTY_INITIALIZER;
         ds_put_format(&s, "%s: ", title);
-        ofpacts_format(ofpacts, ofpacts_len, NULL, &s);
+        struct ofpact_format_params fp = { .s = &s };
+        ofpacts_format(ofpacts, ofpacts_len, &fp);
         oftrace_report(ctx->xin->trace, type, ds_cstr(&s));
         ds_destroy(&s);
     }
@@ -714,7 +715,8 @@  xlate_report_action_set(const struct xlate_ctx *ctx, const char *verb)
         ofpacts_execute_action_set(&action_list, &ctx->action_set);
         if (action_list.size) {
             struct ds s = DS_EMPTY_INITIALIZER;
-            ofpacts_format(action_list.data, action_list.size, NULL, &s);
+            struct ofpact_format_params fp = { .s = &s };
+            ofpacts_format(action_list.data, action_list.size, &fp);
             xlate_report(ctx, OFT_DETAIL, "action set %s: %s",
                          verb, ds_cstr(&s));
             ds_destroy(&s);
@@ -4978,7 +4980,8 @@  xlate_learn_action(struct xlate_ctx *ctx, const struct ofpact_learn *learn)
                 ds_put_cstr(&s, " send_flow_rem");
             }
             ds_put_cstr(&s, " actions=");
-            ofpacts_format(fm.ofpacts, fm.ofpacts_len, NULL, &s);
+            struct ofpact_format_params fp = { .s = &s };
+            ofpacts_format(fm.ofpacts, fm.ofpacts_len, &fp);
             xlate_report(ctx, OFT_DETAIL, "%s", ds_cstr(&s));
             ds_destroy(&s);
         }
@@ -6087,7 +6090,8 @@  do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
 
         if (OVS_UNLIKELY(ctx->xin->trace)) {
             struct ds s = DS_EMPTY_INITIALIZER;
-            ofpacts_format(a, OFPACT_ALIGN(a->len), NULL, &s);
+            struct ofpact_format_params fp = { .s = &s };
+            ofpacts_format(a, OFPACT_ALIGN(a->len), &fp);
             xlate_report(ctx, OFT_ACTION, "%s", ds_cstr(&s));
             ds_destroy(&s);
         }
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index d42acd747acc..71f449f7ddda 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -4399,7 +4399,8 @@  flow_stats_ds(struct ofproto *ofproto, struct rule *rule, struct ds *results)
     ds_put_char(results, ',');
 
     ds_put_cstr(results, "actions=");
-    ofpacts_format(actions->ofpacts, actions->ofpacts_len, NULL, results);
+    struct ofpact_format_params fp = { .s = results };
+    ofpacts_format(actions->ofpacts, actions->ofpacts_len, &fp);
 
     ds_put_cstr(results, "\n");
 }
diff --git a/ovn/controller/ofctrl.c b/ovn/controller/ofctrl.c
index 2fa980ebdae1..264667769296 100644
--- a/ovn/controller/ofctrl.c
+++ b/ovn/controller/ofctrl.c
@@ -685,7 +685,8 @@  ovn_flow_to_string(const struct ovn_flow *f)
     ds_put_format(&s, "priority=%"PRIu16", ", f->priority);
     match_format(&f->match, NULL, &s, OFP_DEFAULT_PRIORITY);
     ds_put_cstr(&s, ", actions=");
-    ofpacts_format(f->ofpacts, f->ofpacts_len, NULL, &s);
+    struct ofpact_format_params fp = { .s = &s };
+    ofpacts_format(f->ofpacts, f->ofpacts_len, &fp);
     return ds_steal_cstr(&s);
 }
 
diff --git a/ovn/utilities/ovn-sbctl.c b/ovn/utilities/ovn-sbctl.c
index 1c2e6843f23c..0cb8ea480438 100644
--- a/ovn/utilities/ovn-sbctl.c
+++ b/ovn/utilities/ovn-sbctl.c
@@ -806,7 +806,8 @@  sbctl_dump_openflow(struct vconn *vconn, const struct uuid *uuid, bool stats)
                 }
 
                 ds_put_format(&s, "%sactions=%s", colors.actions, colors.end);
-                ofpacts_format(fs->ofpacts, fs->ofpacts_len, NULL, &s);
+                struct ofpact_format_params fp = { .s = &s };
+                ofpacts_format(fs->ofpacts, fs->ofpacts_len, &fp);
             }
             printf("    %s\n", ds_cstr(&s));
         }
diff --git a/tests/test-ovn.c b/tests/test-ovn.c
index f9a5085f7185..89b46dbff6fc 100644
--- a/tests/test-ovn.c
+++ b/tests/test-ovn.c
@@ -1263,7 +1263,8 @@  test_parse_actions(struct ovs_cmdl_context *ctx OVS_UNUSED)
             ofpbuf_init(&ofpacts, 0);
             ovnacts_encode(ovnacts.data, ovnacts.size, &ep, &ofpacts);
             struct ds ofpacts_s = DS_EMPTY_INITIALIZER;
-            ofpacts_format(ofpacts.data, ofpacts.size, NULL, &ofpacts_s);
+            struct ofpact_format_params fp = { .s = &ofpacts_s };
+            ofpacts_format(ofpacts.data, ofpacts.size, &fp);
             printf("    encodes as %s\n", ds_cstr(&ofpacts_s));
             ds_destroy(&ofpacts_s);
             ofpbuf_uninit(&ofpacts);
diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
index 953184da4d19..56af5a444d95 100644
--- a/utilities/ovs-ofctl.c
+++ b/utilities/ovs-ofctl.c
@@ -2194,9 +2194,12 @@  ofctl_packet_out(struct ovs_cmdl_context *ctx)
         int i;
 
         ofpbuf_init(&ofpacts, 64);
-        error = ofpacts_parse_actions(ctx->argv[3],
-                                      ports_to_accept(ctx->argv[1]), &ofpacts,
-                                      &usable_protocols);
+        struct ofpact_parse_params pp = {
+            .port_map = ports_to_accept(ctx->argv[1]),
+            .ofpacts = &ofpacts,
+            .usable_protocols = &usable_protocols
+        };
+        error = ofpacts_parse_actions(ctx->argv[3], &pp);
         if (error) {
             ovs_fatal(0, "%s", error);
         }
@@ -3181,8 +3184,11 @@  fte_version_format(const struct fte_state *fte_state, const struct fte *fte,
     }
 
     ds_put_cstr(s, " actions=");
-    ofpacts_format(version->ofpacts, version->ofpacts_len,
-                   fte_state->port_map, s);
+    struct ofpact_format_params fp = {
+        .port_map = fte_state->port_map,
+        .s = s,
+    };
+    ofpacts_format(version->ofpacts, version->ofpacts_len, &fp);
 
     ds_put_char(s, '\n');
 }
@@ -4038,7 +4044,8 @@  ofctl_parse_actions__(const char *version_s, bool instructions)
         /* Print cls_rule. */
         ds_init(&s);
         ds_put_cstr(&s, "actions=");
-        ofpacts_format(ofpacts.data, ofpacts.size, NULL, &s);
+        struct ofpact_format_params fp = { .s = &s };
+        ofpacts_format(ofpacts.data, ofpacts.size, &fp);
         puts(ds_cstr(&s));
         ds_destroy(&s);