[ovs-dev] [of1.1 v3 04/13] Introduce ofpacts, an abstraction of OpenFlow actions.

Pravin Shelar pshelar at nicira.com
Tue Jul 3 23:14:27 UTC 2012


Patch looks pretty good, here are few comments:-

Why have you added explicit OFPACT_END action, why not use action_len
to find END?
------
autopath_to_openflow()
learn_to_openflow()
multipath_to_openflow()

I am not sure these function are not nameed line *_to_nsast().
------
why not store action arg, like vlan id, tunnel id, in network byte
order, so that we do not need to convert it back to network order
while executing actions?
------
nxm_decode(), nxm_decode_discrete() can be removed.
------
can we avoid xmemdup() in ofproto_add_flow()?
------
in handle_flow_stats_request() we need to set ofpacts_len.
------
why is ofpact_fin_timeout added in learn_execute and not in
learn_from_openflow()?
in general do you think it is worth moving more and more stuff from
ofpact_check to ofproto decode step?

------

Thanks,
Pravin.

On Tue, Jun 26, 2012 at 11:53 PM, Ben Pfaff <blp at nicira.com> wrote:
> OpenFlow actions have always been somewhat awkward to handle.
> Moreover, over time we've started creating actions that require more
> complicated parsing.  When we maintain those actions internally in
> their wire format, we end up parsing them multiple times, whenever
> we have to look at the set of actions.
>
> When we add support for OpenFlow 1.1 or later protocols, the situation
> will get worse, because these newer protocols support many of the same
> actions but with different representations.  It becomes unrealistic to
> handle each protocol in its wire format.
>
> This commit adopts a new strategy, by converting OpenFlow actions into
> an internal form from the wire format when they are read, and converting
> them back to the wire format when flows are dumped.  I believe that this
> will be more maintainable over time.
>
> Signed-off-by: Ben Pfaff <blp at nicira.com>
>
> ---
>
> v2:
>
> - Fix confusion between NXAST_RESUBMIT and NXAST_RESUBMIT_TABLE.
>
> - Rename functions for NXAST_ actions to mention nxast in their
>   names instead of openflow10, since they are not OpenFlow 1.0
>   specific.
>
> - Move the code for NXAST_ encoding into a separate function so
>   that later on it can be reused for encoding these actions into
>   OpenFlow 1.1 also.
> ---
>  DESIGN                     |   20 +
>  lib/automake.mk            |    2 +
>  lib/autopath.c             |   70 ++--
>  lib/autopath.h             |   15 +-
>  lib/bundle.c               |  277 ++++++-----
>  lib/bundle.h               |   26 +-
>  lib/compiler.h             |   20 +-
>  lib/learn.c                |  672 ++++++++++++-------------
>  lib/learn.h                |   17 +-
>  lib/learning-switch.c      |   99 ++--
>  lib/multipath.c            |  160 ++++---
>  lib/multipath.h            |   18 +-
>  lib/nx-match.c             |  200 ++++----
>  lib/nx-match.h             |   31 +-
>  lib/ofp-actions.c          | 1213 ++++++++++++++++++++++++++++++++++++++++++++
>  lib/ofp-actions.h          |  479 +++++++++++++++++
>  lib/ofp-parse.c            |  252 +++++-----
>  lib/ofp-parse.h            |    2 +-
>  lib/ofp-print.c            |  291 +----------
>  lib/ofp-print.h            |    2 -
>  lib/ofp-util.c             |  474 ++++--------------
>  lib/ofp-util.h             |   61 +--
>  ofproto/connmgr.c          |   15 +-
>  ofproto/fail-open.c        |   14 +-
>  ofproto/ofproto-dpif.c     |  398 +++++++--------
>  ofproto/ofproto-provider.h |   33 +-
>  ofproto/ofproto.c          |  121 +++---
>  tests/learn.at             |    4 +-
>  tests/test-bundle.c        |   55 +--
>  tests/test-multipath.c     |   11 +-
>  utilities/ovs-ofctl.c      |   54 +-
>  31 files changed, 3119 insertions(+), 1987 deletions(-)
>  create mode 100644 lib/ofp-actions.c
>  create mode 100644 lib/ofp-actions.h
>
> diff --git a/DESIGN b/DESIGN
> index a3a62b2..8dfc1ff 100644
> --- a/DESIGN
> +++ b/DESIGN
> @@ -612,6 +612,26 @@ The following are explicitly *not* supported by in-band control:
>       gateway.
>
>
> +Action Reproduction
> +===================
> +
> +It seems likely that many controllers, at least at startup, use the
> +OpenFlow "flow statistics" request to obtain existing flows, then
> +compare the flows' actions against the actions that they expect to
> +find.  Before version 1.8.0, Open vSwitch always returned exact,
> +byte-for-byte copies of the actions that had been added to the flow
> +table.  The current version of Open vSwitch does not always do this in
> +some exceptional cases.  This section lists the exceptions that
> +controller authors must keep in mind if they compare actual actions
> +against desired actions in a bytewise fashion:
> +
> +       - Open vSwitch zeros padding bytes in action structures,
> +          regardless of their values when the flows were added.
> +
> +Please report other discrepancies, if you notice any, so that we can
> +fix or document them.
> +
> +
>  Suggestions
>  ===========
>
> diff --git a/lib/automake.mk b/lib/automake.mk
> index 9d8b426..641a2f7 100644
> --- a/lib/automake.mk
> +++ b/lib/automake.mk
> @@ -96,6 +96,8 @@ lib_libopenvswitch_a_SOURCES = \
>         lib/nx-match.h \
>         lib/odp-util.c \
>         lib/odp-util.h \
> +       lib/ofp-actions.c \
> +       lib/ofp-actions.h \
>         lib/ofp-errors.c \
>         lib/ofp-errors.h \
>         lib/ofp-parse.c \
> diff --git a/lib/autopath.c b/lib/autopath.c
> index 9511a6d..3d9b350 100644
> --- a/lib/autopath.c
> +++ b/lib/autopath.c
> @@ -1,5 +1,5 @@
>  /*
> - * Copyright (c) 2011 Nicira, Inc.
> + * Copyright (c) 2011, 2012 Nicira, Inc.
>   *
>   * Licensed under the Apache License, Version 2.0 (the "License");
>   * you may not use this file except in compliance with the License.
> @@ -24,6 +24,7 @@
>  #include "flow.h"
>  #include "meta-flow.h"
>  #include "nx-match.h"
> +#include "ofp-actions.h"
>  #include "ofp-errors.h"
>  #include "ofp-util.h"
>  #include "openflow/nicira-ext.h"
> @@ -31,32 +32,21 @@
>
>  VLOG_DEFINE_THIS_MODULE(autopath);
>
> -/* Loads 'ofp_port' into the appropriate register in accordance with the
> - * autopath action. */
>  void
> -autopath_execute(const struct nx_action_autopath *ap, struct flow *flow,
> -                 uint16_t ofp_port)
> -{
> -    struct mf_subfield dst;
> -
> -    nxm_decode(&dst, ap->dst, ap->ofs_nbits);
> -    mf_set_subfield_value(&dst, ofp_port, flow);
> -}
> -
> -void
> -autopath_parse(struct nx_action_autopath *ap, const char *s_)
> +autopath_parse(struct ofpact_autopath *ap, const char *s_)
>  {
>      char *s;
> -    char *id_str, *dst_s, *save_ptr;
> -    struct mf_subfield dst;
>      int id_int;
> +    char *id_str, *dst, *save_ptr;
> +
> +    ofpact_init_AUTOPATH(ap);
>
>      s = xstrdup(s_);
>      save_ptr = NULL;
>      id_str = strtok_r(s, ", ", &save_ptr);
> -    dst_s = strtok_r(NULL, ", ", &save_ptr);
> +    dst = strtok_r(NULL, ", ", &save_ptr);
>
> -    if (!dst_s) {
> +    if (!dst) {
>          ovs_fatal(0, "%s: not enough arguments to autopath action", s_);
>      }
>
> @@ -65,33 +55,51 @@ autopath_parse(struct nx_action_autopath *ap, const char *s_)
>          ovs_fatal(0, "%s: autopath id %d is not in valid range "
>                    "1 to %"PRIu32, s_, id_int, UINT32_MAX);
>      }
> +    ap->port = id_int;
>
> -    mf_parse_subfield(&dst, dst_s);
> -    if (dst.n_bits < 16) {
> +    mf_parse_subfield(&ap->dst, dst);
> +    if (ap->dst.n_bits < 16) {
>          ovs_fatal(0, "%s: %d-bit destination field has %u possible values, "
>                    "less than required 65536",
> -                  s_, dst.n_bits, 1u << dst.n_bits);
> +                  s_, ap->dst.n_bits, 1u << ap->dst.n_bits);
>      }
>
> -    ofputil_init_NXAST_AUTOPATH(ap);
> -    ap->id = htonl(id_int);
> -    ap->ofs_nbits = nxm_encode_ofs_nbits(dst.ofs, dst.n_bits);
> -    ap->dst = htonl(dst.field->nxm_header);
> -
>      free(s);
>  }
>
>  enum ofperr
> -autopath_check(const struct nx_action_autopath *ap, const struct flow *flow)
> +autopath_from_openflow(const struct nx_action_autopath *nap,
> +                       struct ofpact_autopath *autopath)
>  {
> -    struct mf_subfield dst;
> +    ofpact_init_AUTOPATH(autopath);
> +    autopath->dst.field = mf_from_nxm_header(ntohl(nap->dst));
> +    autopath->dst.ofs = nxm_decode_ofs(nap->ofs_nbits);
> +    autopath->dst.n_bits = nxm_decode_n_bits(nap->ofs_nbits);
> +    autopath->port = ntohl(nap->id);
>
> -    nxm_decode(&dst, ap->dst, ap->ofs_nbits);
> -    if (dst.n_bits < 16) {
> +    if (autopath->dst.n_bits < 16) {
>          VLOG_WARN("at least 16 bit destination is required for autopath "
>                    "action.");
>          return OFPERR_OFPBAC_BAD_ARGUMENT;
>      }
>
> -    return mf_check_dst(&dst, flow);
> +    return autopath_check(autopath, NULL);
> +}
> +
> +enum ofperr
> +autopath_check(const struct ofpact_autopath *autopath, const struct flow *flow)
> +{
> +    return mf_check_dst(&autopath->dst, flow);
> +}
> +
> +void
> +autopath_to_openflow(const struct ofpact_autopath *autopath,
> +                     struct ofpbuf *openflow)
> +{
> +    struct nx_action_autopath *ap = ofputil_put_NXAST_AUTOPATH(openflow);
> +
> +    ap->ofs_nbits = nxm_encode_ofs_nbits(autopath->dst.ofs,
> +                                         autopath->dst.n_bits);
> +    ap->dst = htonl(autopath->dst.field->nxm_header);
> +    ap->id = htonl(autopath->port);
>  }
> diff --git a/lib/autopath.h b/lib/autopath.h
> index 480d40a..b23c13f 100644
> --- a/lib/autopath.h
> +++ b/lib/autopath.h
> @@ -1,5 +1,5 @@
>  /*
> - * Copyright (c) 2011 Nicira, Inc.
> + * Copyright (c) 2011, 2012 Nicira, Inc.
>   *
>   * Licensed under the Apache License, Version 2.0 (the "License");
>   * you may not use this file except in compliance with the License.
> @@ -22,15 +22,20 @@
>
>  struct flow;
>  struct nx_action_autopath;
> +struct ofpact_autopath;
> +struct ofpbuf;
>
>  /* NXAST_AUTOPATH  helper functions.
>   *
>   * See include/openflow/nicira-ext.h for NXAST_AUTOPATH specification. */
>
> -void autopath_execute(const struct nx_action_autopath *, struct flow *,
> -                      uint16_t ofp_port);
> -void autopath_parse(struct nx_action_autopath *, const char *);
> -enum ofperr autopath_check(const struct nx_action_autopath *,
> +void autopath_parse(struct ofpact_autopath *, const char *);
> +
> +enum ofperr autopath_from_openflow(const struct nx_action_autopath *,
> +                                   struct ofpact_autopath *);
> +enum ofperr autopath_check(const struct ofpact_autopath *,
>                             const struct flow *);
> +void autopath_to_openflow(const struct ofpact_autopath *,
> +                          struct ofpbuf *openflow);
>
>  #endif /* autopath.h */
> diff --git a/lib/bundle.c b/lib/bundle.c
> index a205974..830c002 100644
> --- a/lib/bundle.c
> +++ b/lib/bundle.c
> @@ -25,6 +25,7 @@
>  #include "meta-flow.h"
>  #include "nx-match.h"
>  #include "ofpbuf.h"
> +#include "ofp-actions.h"
>  #include "ofp-errors.h"
>  #include "ofp-util.h"
>  #include "openflow/nicira-ext.h"
> @@ -35,14 +36,13 @@
>  VLOG_DEFINE_THIS_MODULE(bundle);
>
>  static uint16_t
> -execute_ab(const struct nx_action_bundle *nab,
> +execute_ab(const struct ofpact_bundle *bundle,
>             bool (*slave_enabled)(uint16_t ofp_port, void *aux), void *aux)
>  {
>      size_t i;
>
> -    for (i = 0; i < ntohs(nab->n_slaves); i++) {
> -        uint16_t slave = bundle_get_slave(nab, i);
> -
> +    for (i = 0; i < bundle->n_slaves; i++) {
> +        uint16_t slave = bundle->slaves[i];
>          if (slave_enabled(slave, aux)) {
>              return slave;
>          }
> @@ -52,18 +52,18 @@ execute_ab(const struct nx_action_bundle *nab,
>  }
>
>  static uint16_t
> -execute_hrw(const struct nx_action_bundle *nab, const struct flow *flow,
> +execute_hrw(const struct ofpact_bundle *bundle, const struct flow *flow,
>              bool (*slave_enabled)(uint16_t ofp_port, void *aux), void *aux)
>  {
>      uint32_t flow_hash, best_hash;
>      int best, i;
>
> -    flow_hash = flow_hash_fields(flow, ntohs(nab->fields), ntohs(nab->basis));
> +    flow_hash = flow_hash_fields(flow, bundle->fields, bundle->basis);
>      best = -1;
>      best_hash = 0;
>
> -    for (i = 0; i < ntohs(nab->n_slaves); i++) {
> -        if (slave_enabled(bundle_get_slave(nab, i), aux)) {
> +    for (i = 0; i < bundle->n_slaves; i++) {
> +        if (slave_enabled(bundle->slaves[i], aux)) {
>              uint32_t hash = hash_2words(i, flow_hash);
>
>              if (best < 0 || hash > best_hash) {
> @@ -73,33 +73,26 @@ execute_hrw(const struct nx_action_bundle *nab, const struct flow *flow,
>          }
>      }
>
> -    return best >= 0 ? bundle_get_slave(nab, best) : OFPP_NONE;
> +    return best >= 0 ? bundle->slaves[best] : OFPP_NONE;
>  }
>
> -/* Executes 'nab' on 'flow'.  Uses 'slave_enabled' to determine if the slave
> +/* Executes 'bundle' on 'flow'.  Uses 'slave_enabled' to determine if the slave
>   * designated by 'ofp_port' is up.  Returns the chosen slave, or OFPP_NONE if
>   * none of the slaves are acceptable. */
>  uint16_t
> -bundle_execute(const struct nx_action_bundle *nab, const struct flow *flow,
> +bundle_execute(const struct ofpact_bundle *bundle, const struct flow *flow,
>                 bool (*slave_enabled)(uint16_t ofp_port, void *aux), void *aux)
>  {
> -    switch (ntohs(nab->algorithm)) {
> -    case NX_BD_ALG_HRW: return execute_hrw(nab, flow, slave_enabled, aux);
> -    case NX_BD_ALG_ACTIVE_BACKUP: return execute_ab(nab, slave_enabled, aux);
> -    default: NOT_REACHED();
> -    }
> -}
> +    switch (bundle->algorithm) {
> +    case NX_BD_ALG_HRW:
> +        return execute_hrw(bundle, flow, slave_enabled, aux);
>
> -void
> -bundle_execute_load(const struct nx_action_bundle *nab, struct flow *flow,
> -                    bool (*slave_enabled)(uint16_t ofp_port, void *aux),
> -                    void *aux)
> -{
> -    struct mf_subfield dst;
> +    case NX_BD_ALG_ACTIVE_BACKUP:
> +        return execute_ab(bundle, slave_enabled, aux);
>
> -    nxm_decode(&dst, nab->dst, nab->ofs_nbits);
> -    mf_set_subfield_value(&dst, bundle_execute(nab, flow, slave_enabled, aux),
> -                          flow);
> +    default:
> +        NOT_REACHED();
> +    }
>  }
>
>  /* Checks that 'nab' specifies a bundle action which is supported by this
> @@ -107,41 +100,43 @@ bundle_execute_load(const struct nx_action_bundle *nab, struct flow *flow,
>   * ofputil_check_output_port().  Returns 0 if 'nab' is supported, otherwise an
>   * OFPERR_* error code. */
>  enum ofperr
> -bundle_check(const struct nx_action_bundle *nab, int max_ports,
> -             const struct flow *flow)
> +bundle_from_openflow(const struct nx_action_bundle *nab,
> +                     struct ofpbuf *ofpacts)
>  {
>      static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> -    uint16_t n_slaves, fields, algorithm, subtype;
> +    struct ofpact_bundle *bundle;
> +    uint16_t subtype;
>      uint32_t slave_type;
>      size_t slaves_size, i;
>      enum ofperr error;
>
> +    bundle = ofpact_put_BUNDLE(ofpacts);
> +
>      subtype = ntohs(nab->subtype);
> -    n_slaves = ntohs(nab->n_slaves);
> -    fields = ntohs(nab->fields);
> -    algorithm = ntohs(nab->algorithm);
> +    bundle->n_slaves = ntohs(nab->n_slaves);
> +    bundle->basis = ntohs(nab->basis);
> +    bundle->fields = ntohs(nab->fields);
> +    bundle->algorithm = ntohs(nab->algorithm);
>      slave_type = ntohl(nab->slave_type);
>      slaves_size = ntohs(nab->len) - sizeof *nab;
>
>      error = OFPERR_OFPBAC_BAD_ARGUMENT;
> -    if (!flow_hash_fields_valid(fields)) {
> -        VLOG_WARN_RL(&rl, "unsupported fields %"PRIu16, fields);
> -    } else if (n_slaves > BUNDLE_MAX_SLAVES) {
> +    if (!flow_hash_fields_valid(bundle->fields)) {
> +        VLOG_WARN_RL(&rl, "unsupported fields %d", (int) bundle->fields);
> +    } else if (bundle->n_slaves > BUNDLE_MAX_SLAVES) {
>          VLOG_WARN_RL(&rl, "too may slaves");
> -    } else if (algorithm != NX_BD_ALG_HRW
> -               && algorithm != NX_BD_ALG_ACTIVE_BACKUP) {
> -        VLOG_WARN_RL(&rl, "unsupported algorithm %"PRIu16, algorithm);
> +    } else if (bundle->algorithm != NX_BD_ALG_HRW
> +               && bundle->algorithm != NX_BD_ALG_ACTIVE_BACKUP) {
> +        VLOG_WARN_RL(&rl, "unsupported algorithm %d", (int) bundle->algorithm);
>      } else if (slave_type != NXM_OF_IN_PORT) {
>          VLOG_WARN_RL(&rl, "unsupported slave type %"PRIu16, slave_type);
>      } else {
>          error = 0;
>      }
>
> -    for (i = 0; i < sizeof(nab->zero); i++) {
> -        if (nab->zero[i]) {
> -            VLOG_WARN_RL(&rl, "reserved field is nonzero");
> -            error = OFPERR_OFPBAC_BAD_ARGUMENT;
> -        }
> +    if (!is_all_zeros(nab->zero, sizeof nab->zero)) {
> +        VLOG_WARN_RL(&rl, "reserved field is nonzero");
> +        error = OFPERR_OFPBAC_BAD_ARGUMENT;
>      }
>
>      if (subtype == NXAST_BUNDLE && (nab->ofs_nbits || nab->dst)) {
> @@ -150,34 +145,61 @@ bundle_check(const struct nx_action_bundle *nab, int max_ports,
>      }
>
>      if (subtype == NXAST_BUNDLE_LOAD) {
> -        struct mf_subfield dst;
> +        bundle->dst.field = mf_from_nxm_header(ntohl(nab->dst));
> +        bundle->dst.ofs = nxm_decode_ofs(nab->ofs_nbits);
> +        bundle->dst.n_bits = nxm_decode_n_bits(nab->ofs_nbits);
>
> -        nxm_decode(&dst, nab->dst, nab->ofs_nbits);
> -        if (dst.n_bits < 16) {
> +        if (bundle->dst.n_bits < 16) {
>              VLOG_WARN_RL(&rl, "bundle_load action requires at least 16 bit "
>                           "destination.");
>              error = OFPERR_OFPBAC_BAD_ARGUMENT;
> -        } else if (!error) {
> -            error = mf_check_dst(&dst, flow);
>          }
>      }
>
> -    if (slaves_size < n_slaves * sizeof(ovs_be16)) {
> +    if (slaves_size < bundle->n_slaves * sizeof(ovs_be16)) {
>          VLOG_WARN_RL(&rl, "Nicira action %"PRIu16" only has %zu bytes "
>                       "allocated for slaves.  %zu bytes are required for "
>                       "%"PRIu16" slaves.", subtype, slaves_size,
> -                     n_slaves * sizeof(ovs_be16), n_slaves);
> +                     bundle->n_slaves * sizeof(ovs_be16), bundle->n_slaves);
>          error = OFPERR_OFPBAC_BAD_LEN;
>      }
>
> -    for (i = 0; i < n_slaves; i++) {
> -        uint16_t ofp_port = bundle_get_slave(nab, i);
> -        enum ofperr ofputil_error;
> +    for (i = 0; i < bundle->n_slaves; i++) {
> +        uint16_t ofp_port = ntohs(((ovs_be16 *)(nab + 1))[i]);
> +        ofpbuf_put(ofpacts, &ofp_port, sizeof ofp_port);
> +    }
> +
> +    bundle = ofpacts->l2;
> +    ofpact_update_len(ofpacts, &bundle->ofpact);
> +
> +    if (!error) {
> +        error = bundle_check(bundle, OFPP_MAX, NULL);
> +    }
> +    return error;
> +}
> +
> +enum ofperr
> +bundle_check(const struct ofpact_bundle *bundle, int max_ports,
> +             const struct flow *flow)
> +{
> +    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> +    size_t i;
> +
> +    if (bundle->dst.field) {
> +        enum ofperr error = mf_check_dst(&bundle->dst, flow);
> +        if (error) {
> +            return error;
> +        }
> +    }
> +
> +    for (i = 0; i < bundle->n_slaves; i++) {
> +        uint16_t ofp_port = bundle->slaves[i];
> +        enum ofperr error;
>
> -        ofputil_error = ofputil_check_output_port(ofp_port, max_ports);
> -        if (ofputil_error) {
> +        error = ofputil_check_output_port(ofp_port, max_ports);
> +        if (error) {
>              VLOG_WARN_RL(&rl, "invalid slave %"PRIu16, ofp_port);
> -            error = ofputil_error;
> +            return error;
>          }
>
>          /* Controller slaves are unsupported due to the lack of a max_len
> @@ -185,23 +207,50 @@ bundle_check(const struct nx_action_bundle *nab, int max_ports,
>           * seem to be a real-world use-case for supporting it. */
>          if (ofp_port == OFPP_CONTROLLER) {
>              VLOG_WARN_RL(&rl, "unsupported controller slave");
> -            error = OFPERR_OFPBAC_BAD_OUT_PORT;
> +            return OFPERR_OFPBAC_BAD_OUT_PORT;
>          }
>      }
>
> -    return error;
> +    return 0;
> +}
> +
> +void
> +bundle_to_openflow(const struct ofpact_bundle *bundle, struct ofpbuf *openflow)
> +{
> +    int slaves_len = ROUND_UP(bundle->n_slaves, OFP_ACTION_ALIGN);
> +    struct nx_action_bundle *nab;
> +    ovs_be16 *slaves;
> +    size_t i;
> +
> +    nab = (bundle->dst.field
> +           ? ofputil_put_NXAST_BUNDLE_LOAD(openflow)
> +           : ofputil_put_NXAST_BUNDLE(openflow));
> +    nab->len = htons(ntohs(nab->len) + slaves_len);
> +    nab->algorithm = htons(bundle->algorithm);
> +    nab->fields = htons(bundle->fields);
> +    nab->basis = htons(bundle->basis);
> +    nab->slave_type = htonl(NXM_OF_IN_PORT);
> +    nab->n_slaves = htons(bundle->n_slaves);
> +    if (bundle->dst.field) {
> +        nab->ofs_nbits = nxm_encode_ofs_nbits(bundle->dst.ofs,
> +                                              bundle->dst.n_bits);
> +        nab->dst = htonl(bundle->dst.field->nxm_header);
> +    }
> +
> +    slaves = ofpbuf_put_zeros(openflow, slaves_len);
> +    for (i = 0; i < bundle->n_slaves; i++) {
> +        slaves[i] = htons(bundle->slaves[i]);
> +    }
>  }
>
>  /* Helper for bundle_parse and bundle_parse_load. */
>  static void
> -bundle_parse__(struct ofpbuf *b, const char *s, char **save_ptr,
> +bundle_parse__(const char *s, char **save_ptr,
>                 const char *fields, const char *basis, const char *algorithm,
> -               const char *slave_type, const char *dst_s,
> -               const char *slave_delim)
> +               const char *slave_type, const char *dst,
> +               const char *slave_delim, struct ofpbuf *ofpacts)
>  {
> -    enum ofputil_action_code code;
> -    struct nx_action_bundle *nab;
> -    uint16_t n_slaves;
> +    struct ofpact_bundle *bundle;
>
>      if (!slave_delim) {
>          ovs_fatal(0, "%s: not enough arguments to bundle action", s);
> @@ -212,72 +261,56 @@ bundle_parse__(struct ofpbuf *b, const char *s, char **save_ptr,
>                     s, slave_delim);
>      }
>
> -    code = dst_s ? OFPUTIL_NXAST_BUNDLE_LOAD : OFPUTIL_NXAST_BUNDLE;
> -    b->l2 = ofputil_put_action(code, b);
> +    bundle = ofpact_put_BUNDLE(ofpacts);
>
> -    n_slaves = 0;
>      for (;;) {
> -        ovs_be16 slave_be;
> +        uint16_t slave_port;
>          char *slave;
>
>          slave = strtok_r(NULL, ", [", save_ptr);
> -        if (!slave || n_slaves >= BUNDLE_MAX_SLAVES) {
> +        if (!slave || bundle->n_slaves >= BUNDLE_MAX_SLAVES) {
>              break;
>          }
>
> -        slave_be = htons(atoi(slave));
> -        ofpbuf_put(b, &slave_be, sizeof slave_be);
> +        slave_port = atoi(slave);
> +        ofpbuf_put(ofpacts, &slave_port, sizeof slave_port);
>
> -        n_slaves++;
> +        bundle = ofpacts->l2;
> +        bundle->n_slaves++;
>      }
> +    ofpact_update_len(ofpacts, &bundle->ofpact);
>
> -    /* Slaves array must be multiple of 8 bytes long. */
> -    if (b->size % 8) {
> -        ofpbuf_put_zeros(b, 8 - (b->size % 8));
> -    }
> -
> -    nab = b->l2;
> -    nab->len = htons(b->size - ((char *) b->l2 - (char *) b->data));
> -    nab->n_slaves = htons(n_slaves);
> -    nab->basis = htons(atoi(basis));
> +    bundle->basis = atoi(basis);
>
>      if (!strcasecmp(fields, "eth_src")) {
> -        nab->fields = htons(NX_HASH_FIELDS_ETH_SRC);
> +        bundle->fields = NX_HASH_FIELDS_ETH_SRC;
>      } else if (!strcasecmp(fields, "symmetric_l4")) {
> -        nab->fields = htons(NX_HASH_FIELDS_SYMMETRIC_L4);
> +        bundle->fields = NX_HASH_FIELDS_SYMMETRIC_L4;
>      } else {
>          ovs_fatal(0, "%s: unknown fields `%s'", s, fields);
>      }
>
>      if (!strcasecmp(algorithm, "active_backup")) {
> -        nab->algorithm = htons(NX_BD_ALG_ACTIVE_BACKUP);
> +        bundle->algorithm = NX_BD_ALG_ACTIVE_BACKUP;
>      } else if (!strcasecmp(algorithm, "hrw")) {
> -        nab->algorithm = htons(NX_BD_ALG_HRW);
> +        bundle->algorithm = NX_BD_ALG_HRW;
>      } else {
>          ovs_fatal(0, "%s: unknown algorithm `%s'", s, algorithm);
>      }
>
> -    if (!strcasecmp(slave_type, "ofport")) {
> -        nab->slave_type = htonl(NXM_OF_IN_PORT);
> -    } else {
> +    if (strcasecmp(slave_type, "ofport")) {
>          ovs_fatal(0, "%s: unknown slave_type `%s'", s, slave_type);
>      }
>
> -    if (dst_s) {
> -        struct mf_subfield dst;
> -
> -        mf_parse_subfield(&dst, dst_s);
> -        nab->dst = htonl(dst.field->nxm_header);
> -        nab->ofs_nbits = nxm_encode_ofs_nbits(dst.ofs, dst.n_bits);
> +    if (dst) {
> +        mf_parse_subfield(&bundle->dst, dst);
>      }
> -
> -    b->l2 = NULL;
>  }
>
>  /* Converts a bundle action string contained in 's' to an nx_action_bundle and
>   * stores it in 'b'.  Sets 'b''s l2 pointer to NULL. */
>  void
> -bundle_parse(struct ofpbuf *b, const char *s)
> +bundle_parse(const char *s, struct ofpbuf *ofpacts)
>  {
>      char *fields, *basis, *algorithm, *slave_type, *slave_delim;
>      char *tokstr, *save_ptr;
> @@ -290,15 +323,15 @@ bundle_parse(struct ofpbuf *b, const char *s)
>      slave_type = strtok_r(NULL, ", ", &save_ptr);
>      slave_delim = strtok_r(NULL, ": ", &save_ptr);
>
> -    bundle_parse__(b, s, &save_ptr, fields, basis, algorithm, slave_type, NULL,
> -                   slave_delim);
> +    bundle_parse__(s, &save_ptr, fields, basis, algorithm, slave_type, NULL,
> +                   slave_delim, ofpacts);
>      free(tokstr);
>  }
>
>  /* Converts a bundle_load action string contained in 's' to an nx_action_bundle
>   * and stores it in 'b'.  Sets 'b''s l2 pointer to NULL. */
>  void
> -bundle_parse_load(struct ofpbuf *b, const char *s)
> +bundle_parse_load(const char *s, struct ofpbuf *ofpacts)
>  {
>      char *fields, *basis, *algorithm, *slave_type, *dst, *slave_delim;
>      char *tokstr, *save_ptr;
> @@ -312,22 +345,22 @@ bundle_parse_load(struct ofpbuf *b, const char *s)
>      dst = strtok_r(NULL, ", ", &save_ptr);
>      slave_delim = strtok_r(NULL, ": ", &save_ptr);
>
> -    bundle_parse__(b, s, &save_ptr, fields, basis, algorithm, slave_type, dst,
> -                   slave_delim);
> +    bundle_parse__(s, &save_ptr, fields, basis, algorithm, slave_type, dst,
> +                   slave_delim, ofpacts);
>
>      free(tokstr);
>  }
>
>  /* Appends a human-readable representation of 'nab' to 's'. */
>  void
> -bundle_format(const struct nx_action_bundle *nab, struct ds *s)
> +bundle_format(const struct ofpact_bundle *bundle, struct ds *s)
>  {
> -    const char *action, *fields, *algorithm, *slave_type;
> +    const char *action, *fields, *algorithm;
>      size_t i;
>
> -    fields = flow_hash_fields_to_str(ntohs(nab->fields));
> +    fields = flow_hash_fields_to_str(bundle->fields);
>
> -    switch (ntohs(nab->algorithm)) {
> +    switch (bundle->algorithm) {
>      case NX_BD_ALG_HRW:
>          algorithm = "hrw";
>          break;
> @@ -338,43 +371,23 @@ bundle_format(const struct nx_action_bundle *nab, struct ds *s)
>          algorithm = "<unknown>";
>      }
>
> -    switch (ntohl(nab->slave_type)) {
> -    case NXM_OF_IN_PORT:
> -        slave_type = "ofport";
> -        break;
> -    default:
> -        slave_type = "<unknown>";
> -    }
> -
> -    switch (ntohs(nab->subtype)) {
> -    case NXAST_BUNDLE:
> -        action = "bundle";
> -        break;
> -    case NXAST_BUNDLE_LOAD:
> -        action = "bundle_load";
> -        break;
> -    default:
> -        NOT_REACHED();
> -    }
> +    action = bundle->dst.field ? "bundle_load" : "bundle";
>
>      ds_put_format(s, "%s(%s,%"PRIu16",%s,%s,", action, fields,
> -                  ntohs(nab->basis), algorithm, slave_type);
> -
> -    if (nab->subtype == htons(NXAST_BUNDLE_LOAD)) {
> -        struct mf_subfield dst;
> +                  bundle->basis, algorithm, "ofport");
>
> -        nxm_decode(&dst, nab->dst, nab->ofs_nbits);
> -        mf_format_subfield(&dst, s);
> +    if (bundle->dst.field) {
> +        mf_format_subfield(&bundle->dst, s);
>          ds_put_cstr(s, ",");
>      }
>
>      ds_put_cstr(s, "slaves:");
> -    for (i = 0; i < ntohs(nab->n_slaves); i++) {
> +    for (i = 0; i < bundle->n_slaves; i++) {
>          if (i) {
>              ds_put_cstr(s, ",");
>          }
>
> -        ds_put_format(s, "%"PRIu16, bundle_get_slave(nab, i));
> +        ds_put_format(s, "%"PRIu16, bundle->slaves[i]);
>      }
>
>      ds_put_cstr(s, ")");
> diff --git a/lib/bundle.h b/lib/bundle.h
> index 4c0dff5..6d07b33 100644
> --- a/lib/bundle.h
> +++ b/lib/bundle.h
> @@ -1,4 +1,4 @@
> -/* Copyright (c) 2011 Nicira, Inc.
> +/* Copyright (c) 2011, 2012 Nicira, Inc.
>   *
>   * Licensed under the Apache License, Version 2.0 (the "License");
>   * you may not use this file except in compliance with the License.
> @@ -27,29 +27,23 @@
>
>  struct ds;
>  struct flow;
> +struct ofpact_bundle;
>  struct ofpbuf;
>
>  /* NXAST_BUNDLE helper functions.
>   *
>   * See include/openflow/nicira-ext.h for NXAST_BUNDLE specification. */
>
> -uint16_t bundle_execute(const struct nx_action_bundle *, const struct flow *,
> +uint16_t bundle_execute(const struct ofpact_bundle *, const struct flow *,
>                          bool (*slave_enabled)(uint16_t ofp_port, void *aux),
>                          void *aux);
> -void bundle_execute_load(const struct nx_action_bundle *, struct flow *,
> -                         bool (*slave_enabled)(uint16_t ofp_port, void *aux),
> -                         void *aux);
> -enum ofperr bundle_check(const struct nx_action_bundle *, int max_ports,
> +enum ofperr bundle_from_openflow(const struct nx_action_bundle *,
> +                                 struct ofpbuf *ofpact);
> +enum ofperr bundle_check(const struct ofpact_bundle *, int max_ports,
>                           const struct flow *);
> -void bundle_parse(struct ofpbuf *, const char *);
> -void bundle_parse_load(struct ofpbuf *b, const char *);
> -void bundle_format(const struct nx_action_bundle *, struct ds *);
> -
> -/* Returns the 'i'th slave in 'nab'. */
> -static inline uint16_t
> -bundle_get_slave(const struct nx_action_bundle *nab, size_t i)
> -{
> -    return ntohs(((ovs_be16 *)(nab + 1))[i]);
> -}
> +void bundle_to_openflow(const struct ofpact_bundle *, struct ofpbuf *of10);
> +void bundle_parse(const char *, struct ofpbuf *ofpacts);
> +void bundle_parse_load(const char *, struct ofpbuf *ofpacts);
> +void bundle_format(const struct ofpact_bundle *, struct ds *);
>
>  #endif /* bundle.h */
> diff --git a/lib/compiler.h b/lib/compiler.h
> index 75e8610..27612a7 100644
> --- a/lib/compiler.h
> +++ b/lib/compiler.h
> @@ -1,5 +1,5 @@
>  /*
> - * Copyright (c) 2008, 2009, 2010, 2011 Nicira, Inc.
> + * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
>   *
>   * Licensed under the Apache License, Version 2.0 (the "License");
>   * you may not use this file except in compliance with the License.
> @@ -37,4 +37,22 @@
>  #define SENTINEL(N)
>  #endif
>
> +/* ISO C says that a C implementation may choose any integer type for an enum
> + * that is sufficient to hold all of its values.  Common ABIs (such as the
> + * System V ABI used on i386 GNU/Linux) always use a full-sized "int", even
> + * when a smaller type would suffice.
> + *
> + * In GNU C, "enum __attribute__((packed)) name { ... }" defines 'name' as an
> + * enum compatible with a type that is no bigger than necessary.  This is the
> + * intended use of OVS_PACKED_ENUM.
> + *
> + * OVS_PACKED_ENUM is intended for use only as a space optimization, since it
> + * only works with GCC.  That means that it must not be used in wire protocols
> + * or otherwise exposed outside of a single process. */
> +#if __GNUC__ && !__CHECKER__
> +#define OVS_PACKED_ENUM __attribute__((__packed__))
> +#else
> +#define OVS_PACKED_ENUM
> +#endif
> +
>  #endif /* compiler.h */
> diff --git a/lib/learn.c b/lib/learn.c
> index 5478b74..7dd02d0 100644
> --- a/lib/learn.c
> +++ b/lib/learn.c
> @@ -22,6 +22,7 @@
>  #include "dynamic-string.h"
>  #include "meta-flow.h"
>  #include "nx-match.h"
> +#include "ofp-actions.h"
>  #include "ofp-errors.h"
>  #include "ofp-util.h"
>  #include "ofpbuf.h"
> @@ -46,19 +47,6 @@ get_be32(const void **pp)
>      return value;
>  }
>
> -static uint64_t
> -get_bits(int n_bits, const void **p)
> -{
> -    int n_segs = DIV_ROUND_UP(n_bits, 16);
> -    uint64_t value;
> -
> -    value = 0;
> -    while (n_segs-- > 0) {
> -        value = (value << 16) | ntohs(get_be16(p));
> -    }
> -    return value;
> -}
> -
>  static void
>  get_subfield(int n_bits, const void **p, struct mf_subfield *sf)
>  {
> @@ -90,105 +78,85 @@ learn_min_len(uint16_t header)
>      return min_len;
>  }
>
> -static enum ofperr
> -learn_check_header(uint16_t header, size_t len)
> +/* Converts 'nal' into a "struct ofpact_learn" and appends that struct to
> + * 'ofpacts'.  Returns 0 if successful, otherwise an OFPERR_*. */
> +enum ofperr
> +learn_from_openflow(const struct nx_action_learn *nal, struct ofpbuf *ofpacts)
>  {
> -    int src_type = header & NX_LEARN_SRC_MASK;
> -    int dst_type = header & NX_LEARN_DST_MASK;
> +    struct ofpact_learn *learn;
> +    const void *p, *end;
>
> -    /* Check for valid src and dst type combination. */
> -    if (dst_type == NX_LEARN_DST_MATCH ||
> -        dst_type == NX_LEARN_DST_LOAD ||
> -        (dst_type == NX_LEARN_DST_OUTPUT &&
> -         src_type == NX_LEARN_SRC_FIELD)) {
> -        /* OK. */
> -    } else {
> +    if (nal->pad) {
>          return OFPERR_OFPBAC_BAD_ARGUMENT;
>      }
>
> -    /* Check that the arguments don't overrun the end of the action. */
> -    if (len < learn_min_len(header)) {
> -        return OFPERR_OFPBAC_BAD_LEN;
> -    }
> +    learn = ofpact_put_LEARN(ofpacts);
>
> -    return 0;
> -}
> -
> -/* Checks that 'learn' (which must be at least 'sizeof *learn' bytes long) is a
> - * valid action on 'flow'. */
> -enum ofperr
> -learn_check(const struct nx_action_learn *learn, const struct flow *flow)
> -{
> -    struct cls_rule rule;
> -    const void *p, *end;
> -
> -    cls_rule_init_catchall(&rule, 0);
> +    learn->idle_timeout = ntohs(nal->idle_timeout);
> +    learn->hard_timeout = ntohs(nal->hard_timeout);
> +    learn->priority = ntohs(nal->priority);
> +    learn->cookie = ntohll(nal->cookie);
> +    learn->flags = ntohs(nal->flags);
> +    learn->table_id = nal->table_id;
> +    learn->fin_idle_timeout = ntohs(nal->fin_idle_timeout);
> +    learn->fin_hard_timeout = ntohs(nal->fin_hard_timeout);
>
> -    if (learn->flags & ~htons(OFPFF_SEND_FLOW_REM)
> -        || learn->pad
> -        || learn->table_id == 0xff) {
> +    if (learn->flags & ~OFPFF_SEND_FLOW_REM || learn->table_id == 0xff) {
>          return OFPERR_OFPBAC_BAD_ARGUMENT;
>      }
>
> -    end = (char *) learn + ntohs(learn->len);
> -    for (p = learn + 1; p != end; ) {
> +    end = (char *) nal + ntohs(nal->len);
> +    for (p = nal + 1; p != end; ) {
> +        struct ofpact_learn_spec *spec;
>          uint16_t header = ntohs(get_be16(&p));
> -        int n_bits = header & NX_LEARN_N_BITS_MASK;
> -        int src_type = header & NX_LEARN_SRC_MASK;
> -        int dst_type = header & NX_LEARN_DST_MASK;
> -
> -        enum ofperr error;
> -        uint64_t value;
>
>          if (!header) {
>              break;
>          }
>
> -        error = learn_check_header(header, (char *) end - (char *) p);
> -        if (error) {
> -            return error;
> -        }
> +        spec = ofpbuf_put_zeros(ofpacts, sizeof *spec);
> +        learn = ofpacts->l2;
> +        learn->n_specs++;
>
> -        /* Check the source. */
> -        if (src_type == NX_LEARN_SRC_FIELD) {
> -            struct mf_subfield src;
> +        spec->src_type = header & NX_LEARN_SRC_MASK;
> +        spec->dst_type = header & NX_LEARN_DST_MASK;
> +        spec->n_bits = header & NX_LEARN_N_BITS_MASK;
>
> -            get_subfield(n_bits, &p, &src);
> -            error = mf_check_src(&src, flow);
> -            if (error) {
> -                return error;
> -            }
> -            value = 0;
> +        /* Check for valid src and dst type combination. */
> +        if (spec->dst_type == NX_LEARN_DST_MATCH ||
> +            spec->dst_type == NX_LEARN_DST_LOAD ||
> +            (spec->dst_type == NX_LEARN_DST_OUTPUT &&
> +             spec->src_type == NX_LEARN_SRC_FIELD)) {
> +            /* OK. */
>          } else {
> -            value = get_bits(n_bits, &p);
> +            return OFPERR_OFPBAC_BAD_ARGUMENT;
>          }
>
> -        /* Check the destination. */
> -        if (dst_type == NX_LEARN_DST_MATCH || dst_type == NX_LEARN_DST_LOAD) {
> -            struct mf_subfield dst;
> +        /* Check that the arguments don't overrun the end of the action. */
> +        if ((char *) end - (char *) p < learn_min_len(header)) {
> +            return OFPERR_OFPBAC_BAD_LEN;
> +        }
>
> -            get_subfield(n_bits, &p, &dst);
> -            error = (dst_type == NX_LEARN_DST_LOAD
> -                     ? mf_check_dst(&dst, &rule.flow)
> -                     : mf_check_src(&dst, &rule.flow));
> -            if (error) {
> -                return error;
> -            }
> +        /* Get the source. */
> +        if (spec->src_type == NX_LEARN_SRC_FIELD) {
> +            get_subfield(spec->n_bits, &p, &spec->src);
> +        } else {
> +            int p_bytes = 2 * DIV_ROUND_UP(spec->n_bits, 16);
>
> -            if (dst_type == NX_LEARN_DST_MATCH
> -                && src_type == NX_LEARN_SRC_IMMEDIATE) {
> -                if (n_bits <= 64) {
> -                    mf_set_subfield(&dst, value, &rule);
> -                } else {
> -                    /* We're only setting subfields to allow us to check
> -                     * prerequisites.  No prerequisite depends on the value of
> -                     * a field that is wider than 64 bits.  So just skip
> -                     * setting it entirely. */
> -                    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 12);
> -                }
> -            }
> +            bitwise_copy(p, p_bytes, 0,
> +                         &spec->src_imm, sizeof spec->src_imm, 0,
> +                         spec->n_bits);
> +            p = (const uint8_t *) p + p_bytes;
> +        }
> +
> +        /* Get the destination. */
> +        if (spec->dst_type == NX_LEARN_DST_MATCH ||
> +            spec->dst_type == NX_LEARN_DST_LOAD) {
> +            get_subfield(spec->n_bits, &p, &spec->dst);
>          }
>      }
> +    ofpact_update_len(ofpacts, &learn->ofpact);
> +
>      if (!is_all_zeros(p, (char *) end - (char *) p)) {
>          return OFPERR_OFPBAC_BAD_ARGUMENT;
>      }
> @@ -196,98 +164,50 @@ learn_check(const struct nx_action_learn *learn, const struct flow *flow)
>      return 0;
>  }
>
> -void
> -learn_execute(const struct nx_action_learn *learn, const struct flow *flow,
> -              struct ofputil_flow_mod *fm)
> +/* Checks that 'learn' is a valid action on 'flow'.  Returns 0 if it is valid,
> + * otherwise an OFPERR_*. */
> +enum ofperr
> +learn_check(const struct ofpact_learn *learn, const struct flow *flow)
>  {
> -    const void *p, *end;
> -    struct ofpbuf actions;
> -
> -    cls_rule_init_catchall(&fm->cr, ntohs(learn->priority));
> -    fm->cookie = htonll(0);
> -    fm->cookie_mask = htonll(0);
> -    fm->new_cookie = learn->cookie;
> -    fm->table_id = learn->table_id;
> -    fm->command = OFPFC_MODIFY_STRICT;
> -    fm->idle_timeout = ntohs(learn->idle_timeout);
> -    fm->hard_timeout = ntohs(learn->hard_timeout);
> -    fm->buffer_id = UINT32_MAX;
> -    fm->out_port = OFPP_NONE;
> -    fm->flags = ntohs(learn->flags) & OFPFF_SEND_FLOW_REM;
> -    fm->actions = NULL;
> -    fm->n_actions = 0;
> -
> -    ofpbuf_init(&actions, 64);
> -
> -    if (learn->fin_idle_timeout || learn->fin_hard_timeout) {
> -        struct nx_action_fin_timeout *naft;
> -
> -        naft = ofputil_put_NXAST_FIN_TIMEOUT(&actions);
> -        naft->fin_idle_timeout = learn->fin_idle_timeout;
> -        naft->fin_hard_timeout = learn->fin_hard_timeout;
> -    }
> -
> -    for (p = learn + 1, end = (char *) learn + ntohs(learn->len); p != end; ) {
> -        uint16_t header = ntohs(get_be16(&p));
> -        int n_bits = header & NX_LEARN_N_BITS_MASK;
> -        int src_type = header & NX_LEARN_SRC_MASK;
> -        int dst_type = header & NX_LEARN_DST_MASK;
> -        union mf_subvalue value;
> -
> -        struct mf_subfield dst;
> -        int chunk, ofs;
> -
> -        if (!header) {
> -            break;
> -        }
> -
> -        if (src_type == NX_LEARN_SRC_FIELD) {
> -            struct mf_subfield src;
> +    const struct ofpact_learn_spec *spec;
> +    struct cls_rule rule;
>
> -            get_subfield(n_bits, &p, &src);
> -            mf_read_subfield(&src, flow, &value);
> -        } else {
> -            int p_bytes = 2 * DIV_ROUND_UP(n_bits, 16);
> +    cls_rule_init_catchall(&rule, 0);
> +    for (spec = learn->specs; spec < &learn->specs[learn->n_specs]; spec++) {
> +        enum ofperr error;
>
> -            memset(&value, 0, sizeof value);
> -            bitwise_copy(p, p_bytes, 0,
> -                         &value, sizeof value, 0,
> -                         n_bits);
> -            p = (const uint8_t *) p + p_bytes;
> +        /* Check the source. */
> +        if (spec->src_type == NX_LEARN_SRC_FIELD) {
> +            error = mf_check_src(&spec->src, flow);
> +            if (error) {
> +                return error;
> +            }
>          }
>
> -        switch (dst_type) {
> +        /* Check the destination. */
> +        switch (spec->dst_type) {
>          case NX_LEARN_DST_MATCH:
> -            get_subfield(n_bits, &p, &dst);
> -            mf_write_subfield(&dst, &value, &fm->cr);
> +            error = mf_check_src(&spec->dst, &rule.flow);
> +            if (error) {
> +                return error;
> +            }
> +
> +            mf_write_subfield(&spec->dst, &spec->src_imm, &rule);
>              break;
>
>          case NX_LEARN_DST_LOAD:
> -            get_subfield(n_bits, &p, &dst);
> -            for (ofs = 0; ofs < n_bits; ofs += chunk) {
> -                struct nx_action_reg_load *load;
> -
> -                chunk = MIN(n_bits - ofs, 64);
> -
> -                load = ofputil_put_NXAST_REG_LOAD(&actions);
> -                load->ofs_nbits = nxm_encode_ofs_nbits(dst.ofs + ofs, chunk);
> -                load->dst = htonl(dst.field->nxm_header);
> -                bitwise_copy(&value, sizeof value, ofs,
> -                             &load->value, sizeof load->value, 0,
> -                             chunk);
> +            error = mf_check_dst(&spec->dst, &rule.flow);
> +            if (error) {
> +                return error;
>              }
>              break;
>
>          case NX_LEARN_DST_OUTPUT:
> -            if (n_bits <= 16 || is_all_zeros(value.u8, sizeof value - 2)) {
> -                ofputil_put_OFPAT10_OUTPUT(&actions)->port = value.be16[7];
> -            }
> +            /* Nothing to do. */
>              break;
>          }
>      }
> -
> -    fm->actions = ofpbuf_steal_data(&actions);
> -    fm->n_actions = actions.size / sizeof(struct ofp_action_header);
> +    return 0;
>  }
>
>  static void
> @@ -314,19 +234,150 @@ put_u32(struct ofpbuf *b, uint32_t x)
>      put_be32(b, htonl(x));
>  }
>
> -struct learn_spec {
> -    int n_bits;
> +/* Converts 'learn' into a "struct nx_action_learn" and appends that action to
> + * 'ofpacts'. */
> +void
> +learn_to_openflow(const struct ofpact_learn *learn, struct ofpbuf *openflow)
> +{
> +    const struct ofpact_learn_spec *spec;
> +    struct nx_action_learn *nal;
> +    size_t start_ofs;
> +
> +    start_ofs = openflow->size;
> +    nal = ofputil_put_NXAST_LEARN(openflow);
> +    nal->idle_timeout = htons(learn->idle_timeout);
> +    nal->hard_timeout = htons(learn->hard_timeout);
> +    nal->fin_idle_timeout = htons(learn->fin_idle_timeout);
> +    nal->fin_hard_timeout = htons(learn->fin_hard_timeout);
> +    nal->priority = htons(learn->priority);
> +    nal->cookie = htonll(learn->cookie);
> +    nal->flags = htons(learn->flags);
> +    nal->table_id = learn->table_id;
> +
> +    for (spec = learn->specs; spec < &learn->specs[learn->n_specs]; spec++) {
> +        put_u16(openflow, spec->n_bits | spec->dst_type | spec->src_type);
> +
> +        if (spec->src_type == NX_LEARN_SRC_FIELD) {
> +            put_u32(openflow, spec->src.field->nxm_header);
> +            put_u16(openflow, spec->src.ofs);
> +        } else {
> +            size_t n_dst_bytes = 2 * DIV_ROUND_UP(spec->n_bits, 16);
> +            uint8_t *bits = ofpbuf_put_zeros(openflow, n_dst_bytes);
> +            bitwise_copy(&spec->src_imm, sizeof spec->src_imm, 0,
> +                         bits, n_dst_bytes, 0,
> +                         spec->n_bits);
> +        }
> +
> +        if (spec->dst_type == NX_LEARN_DST_MATCH ||
> +            spec->dst_type == NX_LEARN_DST_LOAD) {
> +            put_u32(openflow, spec->dst.field->nxm_header);
> +            put_u16(openflow, spec->dst.ofs);
> +        }
> +    }
> +
> +    if ((openflow->size - start_ofs) % 8) {
> +        ofpbuf_put_zeros(openflow, 8 - (openflow->size - start_ofs) % 8);
> +    }
> +
> +    nal = ofpbuf_at_assert(openflow, start_ofs, sizeof *nal);
> +    nal->len = htons(openflow->size - start_ofs);
> +}
> +
> +/* Composes 'fm' so that executing it will implement 'learn' given that the
> + * packet being processed has 'flow' as its flow.
> + *
> + * Uses 'ofpacts' to store the flow mod's actions.  The caller must initialize
> + * 'ofpacts' and retains ownership of it.  'fm->ofpacts' will point into the
> + * 'ofpacts' buffer.
> + *
> + * The caller has to actually execute 'fm'. */
> +void
> +learn_execute(const struct ofpact_learn *learn, const struct flow *flow,
> +              struct ofputil_flow_mod *fm, struct ofpbuf *ofpacts)
> +{
> +    const struct ofpact_learn_spec *spec;
>
> -    int src_type;
> -    struct mf_subfield src;
> -    union mf_subvalue src_imm;
> +    cls_rule_init_catchall(&fm->cr, learn->priority);
> +    fm->cookie = htonll(0);
> +    fm->cookie_mask = htonll(0);
> +    fm->new_cookie = htonll(learn->cookie);
> +    fm->table_id = learn->table_id;
> +    fm->command = OFPFC_MODIFY_STRICT;
> +    fm->idle_timeout = learn->idle_timeout;
> +    fm->hard_timeout = learn->hard_timeout;
> +    fm->buffer_id = UINT32_MAX;
> +    fm->out_port = OFPP_NONE;
> +    fm->flags = learn->flags;
> +    fm->ofpacts = NULL;
> +    fm->ofpacts_len = 0;
>
> -    int dst_type;
> -    struct mf_subfield dst;
> -};
> +    if (learn->fin_idle_timeout || learn->fin_hard_timeout) {
> +        struct ofpact_fin_timeout *oft;
> +
> +        oft = ofpact_put_FIN_TIMEOUT(ofpacts);
> +        oft->fin_idle_timeout = learn->fin_idle_timeout;
> +        oft->fin_hard_timeout = learn->fin_hard_timeout;
> +    }
> +
> +    for (spec = learn->specs; spec < &learn->specs[learn->n_specs]; spec++) {
> +        union mf_subvalue value;
> +        int chunk, ofs;
> +
> +        if (spec->src_type == NX_LEARN_SRC_FIELD) {
> +            mf_read_subfield(&spec->src, flow, &value);
> +        } else {
> +            value = spec->src_imm;
> +        }
> +
> +        switch (spec->dst_type) {
> +        case NX_LEARN_DST_MATCH:
> +            mf_write_subfield(&spec->dst, &value, &fm->cr);
> +            break;
> +
> +        case NX_LEARN_DST_LOAD:
> +            for (ofs = 0; ofs < spec->n_bits; ofs += chunk) {
> +                struct ofpact_reg_load *load;
> +                ovs_be64 value_be;
> +
> +                chunk = MIN(spec->n_bits - ofs, 64);
> +
> +                load = ofpact_put_REG_LOAD(ofpacts);
> +                load->dst.field = spec->dst.field;
> +                load->dst.ofs = spec->dst.ofs + ofs;
> +                load->dst.n_bits = chunk;
> +
> +                memset(&value_be, 0, sizeof value_be);
> +                bitwise_copy(&value, sizeof value, ofs,
> +                             &value_be, sizeof value_be, 0,
> +                             chunk);
> +                load->value = ntohll(value_be);
> +            }
> +            break;
> +
> +        case NX_LEARN_DST_OUTPUT:
> +            if (spec->n_bits <= 16
> +                || is_all_zeros(value.u8, sizeof value - 2)) {
> +                uint16_t port = ntohs(value.be16[7]);
> +
> +                if (port < OFPP_MAX
> +                    || port == OFPP_IN_PORT
> +                    || port == OFPP_FLOOD
> +                    || port == OFPP_LOCAL
> +                    || port == OFPP_ALL) {
> +                    ofpact_put_OUTPUT(ofpacts)->port = port;
> +                }
> +            }
> +            break;
> +        }
> +    }
> +    ofpact_put_END(ofpacts);
> +
> +    fm->ofpacts = ofpacts->data;
> +    fm->ofpacts_len = ofpacts->size;
> +}
>
>  static void
> -learn_parse_load_immediate(const char *s, struct learn_spec *spec)
> +learn_parse_load_immediate(const char *s, struct ofpact_learn_spec *spec)
>  {
>      const char *full_s = s;
>      const char *arrow = strstr(s, "->");
> @@ -377,9 +428,8 @@ learn_parse_load_immediate(const char *s, struct learn_spec *spec)
>
>  static void
>  learn_parse_spec(const char *orig, char *name, char *value,
> -                 struct learn_spec *spec)
> +                 struct ofpact_learn_spec *spec)
>  {
> -    memset(spec, 0, sizeof *spec);
>      if (mf_from_name(name)) {
>          const struct mf_field *dst = mf_from_name(name);
>          union mf_value imm;
> @@ -428,17 +478,15 @@ learn_parse_spec(const char *orig, char *name, char *value,
>          if (value[strcspn(value, "[-")] == '-') {
>              learn_parse_load_immediate(value, spec);
>          } else {
> -            struct nx_action_reg_move move;
> +            struct ofpact_reg_move move;
>
>              nxm_parse_reg_move(&move, value);
>
> -            spec->n_bits = ntohs(move.n_bits);
> +            spec->n_bits = move.src.n_bits;
>              spec->src_type = NX_LEARN_SRC_FIELD;
> -            nxm_decode_discrete(&spec->src,
> -                                move.src, move.src_ofs, move.n_bits);
> +            spec->src = move.src;
>              spec->dst_type = NX_LEARN_DST_LOAD;
> -            nxm_decode_discrete(&spec->dst,
> -                                move.dst, move.dst_ofs, move.n_bits);
> +            spec->dst = move.dst;
>          }
>      } else if (!strcmp(name, "output")) {
>          if (mf_parse_subfield(&spec->src, value)[0] != '\0') {
> @@ -455,8 +503,8 @@ learn_parse_spec(const char *orig, char *name, char *value,
>  }
>
>  /* Parses 'arg' as a set of arguments to the "learn" action and appends a
> - * matching NXAST_LEARN action to 'b'.  The format parsed is described in
> - * ovs-ofctl(8).
> + * matching OFPACT_LEARN action to 'ofpacts'.  ovs-ofctl(8) describes the
> + * format parsed.
>   *
>   * Prints an error on stderr and aborts the program if 'arg' syntax is invalid.
>   *
> @@ -466,29 +514,23 @@ learn_parse_spec(const char *orig, char *name, char *value,
>   *
>   * Modifies 'arg'. */
>  void
> -learn_parse(struct ofpbuf *b, char *arg, const struct flow *flow)
> +learn_parse(char *arg, const struct flow *flow, struct ofpbuf *ofpacts)
>  {
>      char *orig = xstrdup(arg);
>      char *name, *value;
> -    enum ofperr error;
> -    size_t learn_ofs;
> -    size_t len;
>
> -    struct nx_action_learn *learn;
> +    struct ofpact_learn *learn;
>      struct cls_rule rule;
> +    enum ofperr error;
>
> -    learn_ofs = b->size;
> -    learn = ofputil_put_NXAST_LEARN(b);
> -    learn->idle_timeout = htons(OFP_FLOW_PERMANENT);
> -    learn->hard_timeout = htons(OFP_FLOW_PERMANENT);
> -    learn->priority = htons(OFP_DEFAULT_PRIORITY);
> -    learn->cookie = htonll(0);
> -    learn->flags = htons(0);
> +    learn = ofpact_put_LEARN(ofpacts);
> +    learn->idle_timeout = OFP_FLOW_PERMANENT;
> +    learn->hard_timeout = OFP_FLOW_PERMANENT;
> +    learn->priority = OFP_DEFAULT_PRIORITY;
>      learn->table_id = 1;
>
>      cls_rule_init_catchall(&rule, 0);
>      while (ofputil_parse_key_value(&arg, &name, &value)) {
> -        learn = ofpbuf_at_assert(b, learn_ofs, sizeof *learn);
>          if (!strcmp(name, "table")) {
>              learn->table_id = atoi(value);
>              if (learn->table_id == 255) {
> @@ -496,73 +538,50 @@ learn_parse(struct ofpbuf *b, char *arg, const struct flow *flow)
>                            orig);
>              }
>          } else if (!strcmp(name, "priority")) {
> -            learn->priority = htons(atoi(value));
> +            learn->priority = atoi(value);
>          } else if (!strcmp(name, "idle_timeout")) {
> -            learn->idle_timeout = htons(atoi(value));
> +            learn->idle_timeout = atoi(value);
>          } else if (!strcmp(name, "hard_timeout")) {
> -            learn->hard_timeout = htons(atoi(value));
> +            learn->hard_timeout = atoi(value);
>          } else if (!strcmp(name, "fin_idle_timeout")) {
> -            learn->fin_idle_timeout = htons(atoi(value));
> +            learn->fin_idle_timeout = atoi(value);
>          } else if (!strcmp(name, "fin_hard_timeout")) {
> -            learn->fin_hard_timeout = htons(atoi(value));
> +            learn->fin_hard_timeout = atoi(value);
>          } else if (!strcmp(name, "cookie")) {
> -            learn->cookie = htonll(strtoull(value, NULL, 0));
> +            learn->cookie = strtoull(value, NULL, 0);
>          } else {
> -            struct learn_spec spec;
> +            struct ofpact_learn_spec *spec;
> +
> +            spec = ofpbuf_put_zeros(ofpacts, sizeof *spec);
> +            learn = ofpacts->l2;
> +            learn->n_specs++;
>
> -            learn_parse_spec(orig, name, value, &spec);
> +            learn_parse_spec(orig, name, value, spec);
>
>              /* Check prerequisites. */
> -            if (spec.src_type == NX_LEARN_SRC_FIELD
> -                && flow && !mf_are_prereqs_ok(spec.src.field, flow)) {
> +            if (spec->src_type == NX_LEARN_SRC_FIELD
> +                && flow && !mf_are_prereqs_ok(spec->src.field, flow)) {
>                  ovs_fatal(0, "%s: cannot specify source field %s because "
>                            "prerequisites are not satisfied",
> -                          orig, spec.src.field->name);
> +                          orig, spec->src.field->name);
>              }
> -            if ((spec.dst_type == NX_LEARN_DST_MATCH
> -                 || spec.dst_type == NX_LEARN_DST_LOAD)
> -                && !mf_are_prereqs_ok(spec.dst.field, &rule.flow)) {
> +            if ((spec->dst_type == NX_LEARN_DST_MATCH
> +                 || spec->dst_type == NX_LEARN_DST_LOAD)
> +                && !mf_are_prereqs_ok(spec->dst.field, &rule.flow)) {
>                  ovs_fatal(0, "%s: cannot specify destination field %s because "
>                            "prerequisites are not satisfied",
> -                          orig, spec.dst.field->name);
> +                          orig, spec->dst.field->name);
>              }
>
>              /* Update 'rule' to allow for satisfying destination
>               * prerequisites. */
> -            if (spec.src_type == NX_LEARN_SRC_IMMEDIATE
> -                && spec.dst_type == NX_LEARN_DST_MATCH) {
> -                mf_write_subfield(&spec.dst, &spec.src_imm, &rule);
> -            }
> -
> -            /* Output the flow_mod_spec. */
> -            put_u16(b, spec.n_bits | spec.src_type | spec.dst_type);
> -            if (spec.src_type == NX_LEARN_SRC_IMMEDIATE) {
> -                int n_bytes = DIV_ROUND_UP(spec.n_bits, 16) * 2;
> -                int ofs = sizeof spec.src_imm - n_bytes;
> -                ofpbuf_put(b, &spec.src_imm.u8[ofs], n_bytes);
> -            } else {
> -                put_u32(b, spec.src.field->nxm_header);
> -                put_u16(b, spec.src.ofs);
> -            }
> -            if (spec.dst_type == NX_LEARN_DST_MATCH ||
> -                spec.dst_type == NX_LEARN_DST_LOAD) {
> -                put_u32(b, spec.dst.field->nxm_header);
> -                put_u16(b, spec.dst.ofs);
> -            } else {
> -                assert(spec.dst_type == NX_LEARN_DST_OUTPUT);
> +            if (spec->src_type == NX_LEARN_SRC_IMMEDIATE
> +                && spec->dst_type == NX_LEARN_DST_MATCH) {
> +                mf_write_subfield(&spec->dst, &spec->src_imm, &rule);
>              }
>          }
>      }
> -
> -    put_u16(b, 0);
> -
> -    len = b->size - learn_ofs;
> -    if (len % 8) {
> -        ofpbuf_put_zeros(b, 8 - len % 8);
> -    }
> -
> -    learn = ofpbuf_at_assert(b, learn_ofs, sizeof *learn);
> -    learn->len = htons(b->size - learn_ofs);
> +    ofpact_update_len(ofpacts, &learn->ofpact);
>
>      /* In theory the above should have caught any errors, but... */
>      if (flow) {
> @@ -574,169 +593,106 @@ learn_parse(struct ofpbuf *b, char *arg, const struct flow *flow)
>      free(orig);
>  }
>
> +static void
> +format_subvalue(const union mf_subvalue *subvalue, struct ds *s)
> +{
> +    int i;
> +
> +    for (i = 0; i < ARRAY_SIZE(subvalue->u8); i++) {
> +        if (subvalue->u8[i]) {
> +            ds_put_format(s, "0x%"PRIx8, subvalue->u8[i]);
> +            for (i++; i < ARRAY_SIZE(subvalue->u8); i++) {
> +                ds_put_format(s, "%02"PRIx8, subvalue->u8[i]);
> +            }
> +            return;
> +        }
> +    }
> +    ds_put_char(s, '0');
> +}
> +
> +/* Appends a description of 'learn' to 's', in the format that ovs-ofctl(8)
> + * describes. */
>  void
> -learn_format(const struct nx_action_learn *learn, struct ds *s)
> +learn_format(const struct ofpact_learn *learn, struct ds *s)
>  {
> +    const struct ofpact_learn_spec *spec;
>      struct cls_rule rule;
> -    const void *p, *end;
>
>      cls_rule_init_catchall(&rule, 0);
>
>      ds_put_format(s, "learn(table=%"PRIu8, learn->table_id);
> -    if (learn->idle_timeout != htons(OFP_FLOW_PERMANENT)) {
> -        ds_put_format(s, ",idle_timeout=%"PRIu16, ntohs(learn->idle_timeout));
> +    if (learn->idle_timeout != OFP_FLOW_PERMANENT) {
> +        ds_put_format(s, ",idle_timeout=%"PRIu16, learn->idle_timeout);
>      }
> -    if (learn->hard_timeout != htons(OFP_FLOW_PERMANENT)) {
> -        ds_put_format(s, ",hard_timeout=%"PRIu16, ntohs(learn->hard_timeout));
> +    if (learn->hard_timeout != OFP_FLOW_PERMANENT) {
> +        ds_put_format(s, ",hard_timeout=%"PRIu16, learn->hard_timeout);
>      }
>      if (learn->fin_idle_timeout) {
> -        ds_put_format(s, ",fin_idle_timeout=%"PRIu16,
> -                      ntohs(learn->fin_idle_timeout));
> +        ds_put_format(s, ",fin_idle_timeout=%"PRIu16, learn->fin_idle_timeout);
>      }
>      if (learn->fin_hard_timeout) {
> -        ds_put_format(s, ",fin_hard_timeout=%"PRIu16,
> -                      ntohs(learn->fin_hard_timeout));
> +        ds_put_format(s, ",fin_hard_timeout=%"PRIu16, learn->fin_hard_timeout);
>      }
> -    if (learn->priority != htons(OFP_DEFAULT_PRIORITY)) {
> -        ds_put_format(s, ",priority=%"PRIu16, ntohs(learn->priority));
> +    if (learn->priority != OFP_DEFAULT_PRIORITY) {
> +        ds_put_format(s, ",priority=%"PRIu16, learn->priority);
>      }
> -    if (learn->flags & htons(OFPFF_SEND_FLOW_REM)) {
> +    if (learn->flags & OFPFF_SEND_FLOW_REM) {
>          ds_put_cstr(s, ",OFPFF_SEND_FLOW_REM");
>      }
> -    if (learn->flags & htons(~OFPFF_SEND_FLOW_REM)) {
> -        ds_put_format(s, ",***flags=%"PRIu16"***",
> -                      ntohs(learn->flags) & ~OFPFF_SEND_FLOW_REM);
> +    if (learn->cookie != 0) {
> +        ds_put_format(s, ",cookie=%#"PRIx64, learn->cookie);
>      }
> -    if (learn->cookie != htonll(0)) {
> -        ds_put_format(s, ",cookie=0x%"PRIx64, ntohll(learn->cookie));
> -    }
> -    if (learn->pad != 0) {
> -        ds_put_cstr(s, ",***nonzero pad***");
> -    }
> -
> -    end = (char *) learn + ntohs(learn->len);
> -    for (p = learn + 1; p != end; ) {
> -        uint16_t header = ntohs(get_be16(&p));
> -        int n_bits = header & NX_LEARN_N_BITS_MASK;
> -
> -        int src_type = header & NX_LEARN_SRC_MASK;
> -        struct mf_subfield src;
> -        const uint8_t *src_value;
> -        int src_value_bytes;
> -
> -        int dst_type = header & NX_LEARN_DST_MASK;
> -        struct mf_subfield dst;
> -
> -        enum ofperr error;
> -        int i;
> -
> -        if (!header) {
> -            break;
> -        }
> -
> -        error = learn_check_header(header, (char *) end - (char *) p);
> -        if (error == OFPERR_OFPBAC_BAD_ARGUMENT) {
> -            ds_put_format(s, ",***bad flow_mod_spec header %"PRIx16"***)",
> -                          header);
> -            return;
> -        } else if (error == OFPERR_OFPBAC_BAD_LEN) {
> -            ds_put_format(s, ",***flow_mod_spec at offset %td is %u bytes "
> -                          "long but only %td bytes are left***)",
> -                          (char *) p - (char *) (learn + 1) - 2,
> -                          learn_min_len(header) + 2,
> -                          (char *) end - (char *) p + 2);
> -            return;
> -        }
> -        assert(!error);
> -
> -        /* Get the source. */
> -        if (src_type == NX_LEARN_SRC_FIELD) {
> -            get_subfield(n_bits, &p, &src);
> -            src_value_bytes = 0;
> -            src_value = NULL;
> -        } else {
> -            src.field = NULL;
> -            src.ofs = 0;
> -            src.n_bits = 0;
> -            src_value_bytes = 2 * DIV_ROUND_UP(n_bits, 16);
> -            src_value = p;
> -            p = (const void *) ((const uint8_t *) p + src_value_bytes);
> -        }
> -
> -        /* Get the destination. */
> -        if (dst_type == NX_LEARN_DST_MATCH || dst_type == NX_LEARN_DST_LOAD) {
> -            get_subfield(n_bits, &p, &dst);
> -        } else {
> -            dst.field = NULL;
> -            dst.ofs = 0;
> -            dst.n_bits = 0;
> -        }
>
> +    for (spec = learn->specs; spec < &learn->specs[learn->n_specs]; spec++) {
>          ds_put_char(s, ',');
>
> -        switch (src_type | dst_type) {
> +        switch (spec->src_type | spec->dst_type) {
>          case NX_LEARN_SRC_IMMEDIATE | NX_LEARN_DST_MATCH:
> -            if (dst.field && dst.ofs == 0 && n_bits == dst.field->n_bits) {
> +            if (spec->dst.ofs == 0
> +                && spec->dst.n_bits == spec->dst.field->n_bits) {
>                  union mf_value value;
> -                uint8_t *bytes = (uint8_t *) &value;
> -
> -                if (src_value_bytes > dst.field->n_bytes) {
> -                    /* The destination field is an odd number of bytes, which
> -                     * got rounded up to a multiple of 2 to be put into the
> -                     * learning action.  Skip over the leading byte, which
> -                     * should be zero anyway.  Otherwise the memcpy() below
> -                     * will overrun the start of 'value'. */
> -                    int diff = src_value_bytes - dst.field->n_bytes;
> -                    src_value += diff;
> -                    src_value_bytes -= diff;
> -                }
>
>                  memset(&value, 0, sizeof value);
> -                memcpy(&bytes[dst.field->n_bytes - src_value_bytes],
> -                       src_value, src_value_bytes);
> -                ds_put_format(s, "%s=", dst.field->name);
> -                mf_format(dst.field, &value, NULL, s);
> +                bitwise_copy(&spec->src_imm, sizeof spec->src_imm, 0,
> +                             &value, spec->dst.field->n_bytes, 0,
> +                             spec->dst.field->n_bits);
> +                ds_put_format(s, "%s=", spec->dst.field->name);
> +                mf_format(spec->dst.field, &value, NULL, s);
>              } else {
> -                mf_format_subfield(&dst, s);
> -                ds_put_cstr(s, "=0x");
> -                for (i = 0; i < src_value_bytes; i++) {
> -                    ds_put_format(s, "%02"PRIx8, src_value[i]);
> -                }
> +                mf_format_subfield(&spec->dst, s);
> +                ds_put_char(s, '=');
> +                format_subvalue(&spec->src_imm, s);
>              }
>              break;
>
>          case NX_LEARN_SRC_FIELD | NX_LEARN_DST_MATCH:
> -            mf_format_subfield(&dst, s);
> -            if (src.field != dst.field || src.ofs != dst.ofs) {
> +            mf_format_subfield(&spec->dst, s);
> +            if (spec->src.field != spec->dst.field ||
> +                spec->src.ofs != spec->dst.ofs) {
>                  ds_put_char(s, '=');
> -                mf_format_subfield(&src, s);
> +                mf_format_subfield(&spec->src, s);
>              }
>              break;
>
>          case NX_LEARN_SRC_IMMEDIATE | NX_LEARN_DST_LOAD:
> -            ds_put_cstr(s, "load:0x");
> -            for (i = 0; i < src_value_bytes; i++) {
> -                ds_put_format(s, "%02"PRIx8, src_value[i]);
> -            }
> +            ds_put_format(s, "load:");
> +            format_subvalue(&spec->src_imm, s);
>              ds_put_cstr(s, "->");
> -            mf_format_subfield(&dst, s);
> +            mf_format_subfield(&spec->dst, s);
>              break;
>
>          case NX_LEARN_SRC_FIELD | NX_LEARN_DST_LOAD:
>              ds_put_cstr(s, "load:");
> -            mf_format_subfield(&src, s);
> +            mf_format_subfield(&spec->src, s);
>              ds_put_cstr(s, "->");
> -            mf_format_subfield(&dst, s);
> +            mf_format_subfield(&spec->dst, s);
>              break;
>
>          case NX_LEARN_SRC_FIELD | NX_LEARN_DST_OUTPUT:
>              ds_put_cstr(s, "output:");
> -            mf_format_subfield(&src, s);
> +            mf_format_subfield(&spec->src, s);
>              break;
>          }
>      }
> -    if (!is_all_zeros(p, (char *) end - (char *) p)) {
> -        ds_put_cstr(s, ",***nonzero trailer***");
> -    }
>      ds_put_char(s, ')');
>  }
> diff --git a/lib/learn.h b/lib/learn.h
> index 2859172..e7fe390 100644
> --- a/lib/learn.h
> +++ b/lib/learn.h
> @@ -1,5 +1,5 @@
>  /*
> - * Copyright (c) 2011 Nicira, Inc.
> + * Copyright (c) 2011, 2012 Nicira, Inc.
>   *
>   * Licensed under the Apache License, Version 2.0 (the "License");
>   * you may not use this file except in compliance with the License.
> @@ -22,6 +22,7 @@
>  struct ds;
>  struct flow;
>  struct ofpbuf;
> +struct ofpact_learn;
>  struct ofputil_flow_mod;
>  struct nx_action_learn;
>
> @@ -30,11 +31,15 @@ struct nx_action_learn;
>   * See include/openflow/nicira-ext.h for NXAST_LEARN specification.
>   */
>
> -enum ofperr learn_check(const struct nx_action_learn *, const struct flow *);
> -void learn_execute(const struct nx_action_learn *, const struct flow *,
> -                   struct ofputil_flow_mod *);
> +enum ofperr learn_from_openflow(const struct nx_action_learn *,
> +                                struct ofpbuf *ofpacts);
> +enum ofperr learn_check(const struct ofpact_learn *, const struct flow *);
> +void learn_to_openflow(const struct ofpact_learn *, struct ofpbuf *openflow);
>
> -void learn_parse(struct ofpbuf *, char *, const struct flow *);
> -void learn_format(const struct nx_action_learn *, struct ds *);
> +void learn_execute(const struct ofpact_learn *, const struct flow *,
> +                   struct ofputil_flow_mod *, struct ofpbuf *ofpacts);
> +
> +void learn_parse(char *, const struct flow *, struct ofpbuf *ofpacts);
> +void learn_format(const struct ofpact_learn *, struct ds *);
>
>  #endif /* learn.h */
> diff --git a/lib/learning-switch.c b/lib/learning-switch.c
> index 6b74f82..916abf3 100644
> --- a/lib/learning-switch.c
> +++ b/lib/learning-switch.c
> @@ -29,6 +29,7 @@
>  #include "hmap.h"
>  #include "mac-learning.h"
>  #include "ofpbuf.h"
> +#include "ofp-actions.h"
>  #include "ofp-errors.h"
>  #include "ofp-parse.h"
>  #include "ofp-print.h"
> @@ -56,6 +57,7 @@ struct lswitch {
>       * Otherwise, the switch processes every packet. */
>      int max_idle;
>
> +    enum ofputil_protocol protocol;
>      unsigned long long int datapath_id;
>      time_t last_features_request;
>      struct mac_learning *ml;    /* NULL to act as hub instead of switch. */
> @@ -81,7 +83,7 @@ static void send_features_request(struct lswitch *, struct rconn *);
>  static enum ofperr process_switch_features(struct lswitch *,
>                                             struct ofp_switch_features *);
>  static void process_packet_in(struct lswitch *, struct rconn *,
> -                              const struct ofp_packet_in *);
> +                              const struct ofp_header *);
>  static void process_echo_request(struct lswitch *, struct rconn *,
>                                   const struct ofp_header *);
>
> @@ -92,6 +94,7 @@ static void process_echo_request(struct lswitch *, struct rconn *,
>  struct lswitch *
>  lswitch_create(struct rconn *rconn, const struct lswitch_config *cfg)
>  {
> +    enum ofputil_protocol protocol;
>      struct lswitch *sw;
>
>      sw = xzalloc(sizeof *sw);
> @@ -139,18 +142,13 @@ lswitch_create(struct rconn *rconn, const struct lswitch_config *cfg)
>      sw->queued = rconn_packet_counter_create();
>      send_features_request(sw, rconn);
>
> +    protocol = ofputil_protocol_from_ofp_version(rconn_get_version(rconn));
>      if (cfg->default_flows) {
>          enum ofputil_protocol usable_protocols;
> -        enum ofputil_protocol protocol;
>          struct ofpbuf *msg = NULL;
> -        int ofp_version;
>          int error = 0;
>          size_t i;
>
> -        /* Figure out the initial protocol on the connection. */
> -        ofp_version = rconn_get_version(rconn);
> -        protocol = ofputil_protocol_from_ofp_version(ofp_version);
> -
>          /* If the initial protocol isn't good enough for default_flows, then
>           * pick one that will work and encode messages to set up that
>           * protocol.
> @@ -181,6 +179,7 @@ lswitch_create(struct rconn *rconn, const struct lswitch_config *cfg)
>                           rconn_get_name(rconn), strerror(error));
>          }
>      }
> +    sw->protocol = protocol;
>
>      return sw;
>  }
> @@ -250,6 +249,7 @@ lswitch_process_packet(struct lswitch *sw, struct rconn *rconn,
>          break;
>
>      case OFPUTIL_OFPT_PACKET_IN:
> +    case OFPUTIL_NXT_PACKET_IN:
>          process_packet_in(sw, rconn, msg->data);
>          break;
>
> @@ -292,7 +292,6 @@ lswitch_process_packet(struct lswitch *sw, struct rconn *rconn,
>      case OFPUTIL_NXT_FLOW_MOD_TABLE_ID:
>      case OFPUTIL_NXT_SET_FLOW_FORMAT:
>      case OFPUTIL_NXT_SET_PACKET_IN_FORMAT:
> -    case OFPUTIL_NXT_PACKET_IN:
>      case OFPUTIL_NXT_FLOW_MOD:
>      case OFPUTIL_NXT_FLOW_REMOVED:
>      case OFPUTIL_NXT_FLOW_AGE:
> @@ -439,67 +438,58 @@ get_queue_id(const struct lswitch *sw, uint16_t in_port)
>
>  static void
>  process_packet_in(struct lswitch *sw, struct rconn *rconn,
> -                  const struct ofp_packet_in *opi)
> +                  const struct ofp_header *oh)
>  {
> -    uint16_t in_port = ntohs(opi->in_port);
> +    struct ofputil_packet_in pi;
>      uint32_t queue_id;
>      uint16_t out_port;
>
> -    struct ofp_action_header actions[2];
> -    size_t actions_len;
> +    uint64_t ofpacts_stub[64 / 8];
> +    struct ofpbuf ofpacts;
>
>      struct ofputil_packet_out po;
> +    enum ofperr error;
>
> -    size_t pkt_ofs, pkt_len;
>      struct ofpbuf pkt;
>      struct flow flow;
>
> +    error = ofputil_decode_packet_in(&pi, oh);
> +    if (error) {
> +        VLOG_WARN_RL(&rl, "failed to decode packet-in: %s",
> +                     ofperr_to_string(error));
> +        return;
> +    }
> +
>      /* Ignore packets sent via output to OFPP_CONTROLLER.  This library never
>       * uses such an action.  You never know what experiments might be going on,
>       * though, and it seems best not to interfere with them. */
> -    if (opi->reason != OFPR_NO_MATCH) {
> +    if (pi.reason != OFPR_NO_MATCH) {
>          return;
>      }
>
>      /* Extract flow data from 'opi' into 'flow'. */
> -    pkt_ofs = offsetof(struct ofp_packet_in, data);
> -    pkt_len = ntohs(opi->header.length) - pkt_ofs;
> -    ofpbuf_use_const(&pkt, opi->data, pkt_len);
> -    flow_extract(&pkt, 0, 0, in_port, &flow);
> +    ofpbuf_use_const(&pkt, pi.packet, pi.packet_len);
> +    flow_extract(&pkt, 0, pi.fmd.tun_id, pi.fmd.in_port, &flow);
>
>      /* Choose output port. */
>      out_port = lswitch_choose_destination(sw, &flow);
>
>      /* Make actions. */
> -    queue_id = get_queue_id(sw, in_port);
> +    queue_id = get_queue_id(sw, pi.fmd.in_port);
> +    ofpbuf_use_stack(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
>      if (out_port == OFPP_NONE) {
> -        actions_len = 0;
> +        /* No actions. */
>      } else if (queue_id == UINT32_MAX || out_port >= OFPP_MAX) {
> -        struct ofp_action_output oao;
> -
> -        memset(&oao, 0, sizeof oao);
> -        oao.type = htons(OFPAT10_OUTPUT);
> -        oao.len = htons(sizeof oao);
> -        oao.port = htons(out_port);
> -
> -        memcpy(actions, &oao, sizeof oao);
> -        actions_len = sizeof oao;
> +        ofpact_put_OUTPUT(&ofpacts)->port = out_port;
>      } else {
> -        struct ofp_action_enqueue oae;
> -
> -        memset(&oae, 0, sizeof oae);
> -        oae.type = htons(OFPAT10_ENQUEUE);
> -        oae.len = htons(sizeof oae);
> -        oae.port = htons(out_port);
> -        oae.queue_id = htonl(queue_id);
> -
> -        memcpy(actions, &oae, sizeof oae);
> -        actions_len = sizeof oae;
> +        struct ofpact_enqueue *enqueue = ofpact_put_ENQUEUE(&ofpacts);
> +        enqueue->port = out_port;
> +        enqueue->queue = queue_id;
>      }
> -    assert(actions_len <= sizeof actions);
> +    ofpact_put_END(&ofpacts);
>
>      /* Prepare packet_out in case we need one. */
> -    po.buffer_id = ntohl(opi->buffer_id);
> +    po.buffer_id = pi.buffer_id;
>      if (po.buffer_id == UINT32_MAX) {
>          po.packet = pkt.data;
>          po.packet_len = pkt.size;
> @@ -507,31 +497,38 @@ process_packet_in(struct lswitch *sw, struct rconn *rconn,
>          po.packet = NULL;
>          po.packet_len = 0;
>      }
> -    po.in_port = in_port;
> -    po.actions = (union ofp_action *) actions;
> -    po.n_actions = actions_len / sizeof *actions;
> +    po.in_port = pi.fmd.in_port;
> +    po.ofpacts = ofpacts.data;
> +    po.ofpacts_len = ofpacts.size;
>
>      /* Send the packet, and possibly the whole flow, to the output port. */
>      if (sw->max_idle >= 0 && (!sw->ml || out_port != OFPP_FLOOD)) {
> +        struct ofputil_flow_mod fm;
>          struct ofpbuf *buffer;
> -        struct cls_rule rule;
>
>          /* The output port is known, or we always flood everything, so add a
>           * new flow. */
> -        cls_rule_init(&flow, &sw->wc, 0, &rule);
> -        buffer = make_add_flow(&rule, ntohl(opi->buffer_id),
> -                               sw->max_idle, actions_len);
> -        ofpbuf_put(buffer, actions, actions_len);
> +        memset(&fm, 0, sizeof fm);
> +        cls_rule_init(&flow, &sw->wc, 0, &fm.cr);
> +        fm.table_id = 0xff;
> +        fm.command = OFPFC_ADD;
> +        fm.idle_timeout = sw->max_idle;
> +        fm.buffer_id = pi.buffer_id;
> +        fm.out_port = OFPP_NONE;
> +        fm.ofpacts = ofpacts.data;
> +        fm.ofpacts_len = ofpacts.size;
> +        buffer = ofputil_encode_flow_mod(&fm, sw->protocol);
> +
>          queue_tx(sw, rconn, buffer);
>
>          /* If the switch didn't buffer the packet, we need to send a copy. */
> -        if (ntohl(opi->buffer_id) == UINT32_MAX && actions_len > 0) {
> +        if (pi.buffer_id == UINT32_MAX && out_port != OFPP_NONE) {
>              queue_tx(sw, rconn, ofputil_encode_packet_out(&po));
>          }
>      } else {
>          /* We don't know that MAC, or we don't set up flows.  Send along the
>           * packet without setting up a flow. */
> -        if (ntohl(opi->buffer_id) != UINT32_MAX || actions_len > 0) {
> +        if (pi.buffer_id != UINT32_MAX || out_port != OFPP_NONE) {
>              queue_tx(sw, rconn, ofputil_encode_packet_out(&po));
>          }
>      }
> diff --git a/lib/multipath.c b/lib/multipath.c
> index 8d93211..c0a5723 100644
> --- a/lib/multipath.c
> +++ b/lib/multipath.c
> @@ -22,8 +22,8 @@
>  #include <sys/types.h>
>  #include <netinet/in.h>
>  #include "dynamic-string.h"
> -#include "meta-flow.h"
>  #include "nx-match.h"
> +#include "ofp-actions.h"
>  #include "ofp-errors.h"
>  #include "ofp-util.h"
>  #include "openflow/nicira-ext.h"
> @@ -34,37 +34,67 @@ VLOG_DEFINE_THIS_MODULE(multipath);
>
>  static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
>
> -/* multipath_check(). */
> +/* Converts 'nam' into 'mp'.  Returns 0 if successful, otherwise an
> + * OFPERR_*. */
>  enum ofperr
> -multipath_check(const struct nx_action_multipath *mp, const struct flow *flow)
> +multipath_from_openflow(const struct nx_action_multipath *nam,
> +                        struct ofpact_multipath *mp)
>  {
> -    uint32_t n_links = ntohs(mp->max_link) + 1;
> +    uint32_t n_links = ntohs(nam->max_link) + 1;
>      size_t min_n_bits = log_2_ceil(n_links);
> -    struct mf_subfield dst;
> -    enum ofperr error;
>
> -    nxm_decode(&dst, mp->dst, mp->ofs_nbits);
> -    error = mf_check_dst(&dst, flow);
> -    if (error) {
> -        return error;
> -    }
> -
> -    if (!flow_hash_fields_valid(ntohs(mp->fields))) {
> -        VLOG_WARN_RL(&rl, "unsupported fields %"PRIu16, ntohs(mp->fields));
> -    } else if (mp->algorithm != htons(NX_MP_ALG_MODULO_N)
> -               && mp->algorithm != htons(NX_MP_ALG_HASH_THRESHOLD)
> -               && mp->algorithm != htons(NX_MP_ALG_HRW)
> -               && mp->algorithm != htons(NX_MP_ALG_ITER_HASH)) {
> -        VLOG_WARN_RL(&rl, "unsupported algorithm %"PRIu16,
> -                     ntohs(mp->algorithm));
> -    } else if (dst.n_bits < min_n_bits) {
> +    ofpact_init_MULTIPATH(mp);
> +    mp->fields = ntohs(nam->fields);
> +    mp->basis = ntohs(nam->basis);
> +    mp->algorithm = ntohs(nam->algorithm);
> +    mp->max_link = ntohs(nam->max_link);
> +    mp->arg = ntohl(nam->arg);
> +    mp->dst.field = mf_from_nxm_header(ntohl(nam->dst));
> +    mp->dst.ofs = nxm_decode_ofs(nam->ofs_nbits);
> +    mp->dst.n_bits = nxm_decode_n_bits(nam->ofs_nbits);
> +
> +    if (!flow_hash_fields_valid(mp->fields)) {
> +        VLOG_WARN_RL(&rl, "unsupported fields %d", (int) mp->fields);
> +        return OFPERR_OFPBAC_BAD_ARGUMENT;
> +    } else if (mp->algorithm != NX_MP_ALG_MODULO_N
> +               && mp->algorithm != NX_MP_ALG_HASH_THRESHOLD
> +               && mp->algorithm != NX_MP_ALG_HRW
> +               && mp->algorithm != NX_MP_ALG_ITER_HASH) {
> +        VLOG_WARN_RL(&rl, "unsupported algorithm %d", (int) mp->algorithm);
> +        return OFPERR_OFPBAC_BAD_ARGUMENT;
> +    } else if (mp->dst.n_bits < min_n_bits) {
>          VLOG_WARN_RL(&rl, "multipath action requires at least %zu bits for "
>                       "%"PRIu32" links", min_n_bits, n_links);
> -    } else {
> -        return 0;
> +        return OFPERR_OFPBAC_BAD_ARGUMENT;
>      }
>
> -    return OFPERR_OFPBAC_BAD_ARGUMENT;
> +    return multipath_check(mp, NULL);
> +}
> +
> +/* Checks that 'mp' is valid on flow.  Returns 0 if it is valid, otherwise an
> + * OFPERR_*. */
> +enum ofperr
> +multipath_check(const struct ofpact_multipath *mp,
> +                const struct flow *flow)
> +{
> +    return mf_check_dst(&mp->dst, flow);
> +}
> +
> +/* Converts 'mp' into an OpenFlow NXAST_MULTIPATH action, which it appends to
> + * 'openflow'. */
> +void
> +multipath_to_openflow(const struct ofpact_multipath *mp,
> +                      struct ofpbuf *openflow)
> +{
> +    struct nx_action_multipath *nam = ofputil_put_NXAST_MULTIPATH(openflow);
> +
> +    nam->fields = htons(mp->fields);
> +    nam->basis = htons(mp->basis);
> +    nam->algorithm = htons(mp->algorithm);
> +    nam->max_link = htons(mp->max_link);
> +    nam->arg = htonl(mp->arg);
> +    nam->ofs_nbits = nxm_encode_ofs_nbits(mp->dst.ofs, mp->dst.n_bits);
> +    nam->dst = htonl(mp->dst.field->nxm_header);
>  }
>
>  /* multipath_execute(). */
> @@ -72,19 +102,17 @@ multipath_check(const struct nx_action_multipath *mp, const struct flow *flow)
>  static uint16_t multipath_algorithm(uint32_t hash, enum nx_mp_algorithm,
>                                      unsigned int n_links, unsigned int arg);
>
> +/* Executes 'mp' based on the current contents of 'flow', writing the results
> + * back into 'flow'. */
>  void
> -multipath_execute(const struct nx_action_multipath *mp, struct flow *flow)
> +multipath_execute(const struct ofpact_multipath *mp, struct flow *flow)
>  {
>      /* Calculate value to store. */
> -    uint32_t hash = flow_hash_fields(flow, ntohs(mp->fields),
> -                                     ntohs(mp->basis));
> -    uint16_t link = multipath_algorithm(hash, ntohs(mp->algorithm),
> -                                        ntohs(mp->max_link) + 1,
> -                                        ntohl(mp->arg));
> -    struct mf_subfield dst;
> -
> -    nxm_decode(&dst, mp->dst, mp->ofs_nbits);
> -    mf_set_subfield_value(&dst, link, flow);
> +    uint32_t hash = flow_hash_fields(flow, mp->fields, mp->basis);
> +    uint16_t link = multipath_algorithm(hash, mp->algorithm,
> +                                        mp->max_link + 1, mp->arg);
> +
> +    nxm_reg_load(&mp->dst, link, flow);
>  }
>
>  static uint16_t
> @@ -162,15 +190,17 @@ multipath_algorithm(uint32_t hash, enum nx_mp_algorithm algorithm,
>      NOT_REACHED();
>  }
>
> -/* multipath_parse(). */
> -
> +/* Parses 's_' as a set of arguments to the "multipath" action and initializes
> + * 'mp' accordingly.  ovs-ofctl(8) describes the format parsed.
> + *
> + * Prints an error on stderr and aborts the program if 's_' syntax is
> + * invalid. */
>  void
> -multipath_parse(struct nx_action_multipath *mp, const char *s_)
> +multipath_parse(struct ofpact_multipath *mp, const char *s_)
>  {
>      char *s = xstrdup(s_);
>      char *save_ptr = NULL;
> -    char *fields, *basis, *algorithm, *n_links_str, *arg, *dst_s;
> -    struct mf_subfield dst;
> +    char *fields, *basis, *algorithm, *n_links_str, *arg, *dst;
>      int n_links;
>
>      fields = strtok_r(s, ", ", &save_ptr);
> @@ -178,28 +208,28 @@ multipath_parse(struct nx_action_multipath *mp, const char *s_)
>      algorithm = strtok_r(NULL, ", ", &save_ptr);
>      n_links_str = strtok_r(NULL, ", ", &save_ptr);
>      arg = strtok_r(NULL, ", ", &save_ptr);
> -    dst_s = strtok_r(NULL, ", ", &save_ptr);
> -    if (!dst_s) {
> +    dst = strtok_r(NULL, ", ", &save_ptr);
> +    if (!dst) {
>          ovs_fatal(0, "%s: not enough arguments to multipath action", s_);
>      }
>
> -    ofputil_init_NXAST_MULTIPATH(mp);
> +    ofpact_init_MULTIPATH(mp);
>      if (!strcasecmp(fields, "eth_src")) {
> -        mp->fields = htons(NX_HASH_FIELDS_ETH_SRC);
> +        mp->fields = NX_HASH_FIELDS_ETH_SRC;
>      } else if (!strcasecmp(fields, "symmetric_l4")) {
> -        mp->fields = htons(NX_HASH_FIELDS_SYMMETRIC_L4);
> +        mp->fields = NX_HASH_FIELDS_SYMMETRIC_L4;
>      } else {
>          ovs_fatal(0, "%s: unknown fields `%s'", s_, fields);
>      }
> -    mp->basis = htons(atoi(basis));
> +    mp->basis = atoi(basis);
>      if (!strcasecmp(algorithm, "modulo_n")) {
> -        mp->algorithm = htons(NX_MP_ALG_MODULO_N);
> +        mp->algorithm = NX_MP_ALG_MODULO_N;
>      } else if (!strcasecmp(algorithm, "hash_threshold")) {
> -        mp->algorithm = htons(NX_MP_ALG_HASH_THRESHOLD);
> +        mp->algorithm = NX_MP_ALG_HASH_THRESHOLD;
>      } else if (!strcasecmp(algorithm, "hrw")) {
> -        mp->algorithm = htons(NX_MP_ALG_HRW);
> +        mp->algorithm = NX_MP_ALG_HRW;
>      } else if (!strcasecmp(algorithm, "iter_hash")) {
> -        mp->algorithm = htons(NX_MP_ALG_ITER_HASH);
> +        mp->algorithm = NX_MP_ALG_ITER_HASH;
>      } else {
>          ovs_fatal(0, "%s: unknown algorithm `%s'", s_, algorithm);
>      }
> @@ -208,34 +238,29 @@ multipath_parse(struct nx_action_multipath *mp, const char *s_)
>          ovs_fatal(0, "%s: n_links %d is not in valid range 1 to 65536",
>                    s_, n_links);
>      }
> -    mp->max_link = htons(n_links - 1);
> -    mp->arg = htonl(atoi(arg));
> +    mp->max_link = n_links - 1;
> +    mp->arg = atoi(arg);
>
> -    mf_parse_subfield(&dst, dst_s);
> -    if (dst.n_bits < 16 && n_links > (1u << dst.n_bits)) {
> +    mf_parse_subfield(&mp->dst, dst);
> +    if (mp->dst.n_bits < 16 && n_links > (1u << mp->dst.n_bits)) {
>          ovs_fatal(0, "%s: %d-bit destination field has %u possible values, "
>                    "less than specified n_links %d",
> -                  s_, dst.n_bits, 1u << dst.n_bits, n_links);
> +                  s_, mp->dst.n_bits, 1u << mp->dst.n_bits, n_links);
>      }
> -    mp->ofs_nbits = nxm_encode_ofs_nbits(dst.ofs, dst.n_bits);
> -    mp->dst = htonl(dst.field->nxm_header);
>
>      free(s);
>  }
>
> +/* Appends a description of 'mp' to 's', in the format that ovs-ofctl(8)
> + * describes. */
>  void
> -multipath_format(const struct nx_action_multipath *mp, struct ds *s)
> +multipath_format(const struct ofpact_multipath *mp, struct ds *s)
>  {
>      const char *fields, *algorithm;
>
> -    uint16_t mp_fields    = ntohs(mp->fields);
> -    uint16_t mp_algorithm = ntohs(mp->algorithm);
> -
> -    struct mf_subfield dst;
> -
> -    fields = flow_hash_fields_to_str(mp_fields);
> +    fields = flow_hash_fields_to_str(mp->fields);
>
> -    switch ((enum nx_mp_algorithm) mp_algorithm) {
> +    switch (mp->algorithm) {
>      case NX_MP_ALG_MODULO_N:
>          algorithm = "modulo_n";
>          break;
> @@ -253,9 +278,8 @@ multipath_format(const struct nx_action_multipath *mp, struct ds *s)
>      }
>
>      ds_put_format(s, "multipath(%s,%"PRIu16",%s,%d,%"PRIu16",",
> -                  fields, ntohs(mp->basis), algorithm, ntohs(mp->max_link) + 1,
> -                  ntohl(mp->arg));
> -    nxm_decode(&dst, mp->dst, mp->ofs_nbits);
> -    mf_format_subfield(&dst, s);
> +                  fields, mp->basis, algorithm, mp->max_link + 1,
> +                  mp->arg);
> +    mf_format_subfield(&mp->dst, s);
>      ds_put_char(s, ')');
>  }
> diff --git a/lib/multipath.h b/lib/multipath.h
> index 2cd646c..4e45d6c 100644
> --- a/lib/multipath.h
> +++ b/lib/multipath.h
> @@ -1,5 +1,5 @@
>  /*
> - * Copyright (c) 2010, 2011 Nicira, Inc.
> + * Copyright (c) 2010, 2011, 2012 Nicira, Inc.
>   *
>   * Licensed under the Apache License, Version 2.0 (the "License");
>   * you may not use this file except in compliance with the License.
> @@ -23,18 +23,24 @@
>  struct ds;
>  struct flow;
>  struct nx_action_multipath;
> -struct nx_action_reg_move;
> +struct ofpact_multipath;
> +struct ofpbuf;
>
>  /* NXAST_MULTIPATH helper functions.
>   *
>   * See include/openflow/nicira-ext.h for NXAST_MULTIPATH specification.
>   */
>
> -enum ofperr multipath_check(const struct nx_action_multipath *,
> +enum ofperr multipath_from_openflow(const struct nx_action_multipath *,
> +                                    struct ofpact_multipath *);
> +enum ofperr multipath_check(const struct ofpact_multipath *,
>                              const struct flow *);
> -void multipath_execute(const struct nx_action_multipath *, struct flow *);
> +void multipath_to_openflow(const struct ofpact_multipath *,
> +                           struct ofpbuf *openflow);
>
> -void multipath_parse(struct nx_action_multipath *, const char *);
> -void multipath_format(const struct nx_action_multipath *, struct ds *);
> +void multipath_execute(const struct ofpact_multipath *, struct flow *);
> +
> +void multipath_parse(struct ofpact_multipath *, const char *);
> +void multipath_format(const struct ofpact_multipath *, struct ds *);
>
>  #endif /* multipath.h */
> diff --git a/lib/nx-match.c b/lib/nx-match.c
> index 92e94f8..061b6b0 100644
> --- a/lib/nx-match.c
> +++ b/lib/nx-match.c
> @@ -23,6 +23,7 @@
>  #include "classifier.h"
>  #include "dynamic-string.h"
>  #include "meta-flow.h"
> +#include "ofp-actions.h"
>  #include "ofp-errors.h"
>  #include "ofp-util.h"
>  #include "ofpbuf.h"
> @@ -789,180 +790,185 @@ nx_match_from_string(const char *s, struct ofpbuf *b)
>  }
>
>  void
> -nxm_parse_reg_move(struct nx_action_reg_move *move, const char *s)
> +nxm_parse_reg_move(struct ofpact_reg_move *move, const char *s)
>  {
>      const char *full_s = s;
> -    struct mf_subfield src, dst;
>
> -    s = mf_parse_subfield(&src, s);
> +    s = mf_parse_subfield(&move->src, s);
>      if (strncmp(s, "->", 2)) {
>          ovs_fatal(0, "%s: missing `->' following source", full_s);
>      }
>      s += 2;
> -    s = mf_parse_subfield(&dst, s);
> +    s = mf_parse_subfield(&move->dst, s);
>      if (*s != '\0') {
>          ovs_fatal(0, "%s: trailing garbage following destination", full_s);
>      }
>
> -    if (src.n_bits != dst.n_bits) {
> +    if (move->src.n_bits != move->dst.n_bits) {
>          ovs_fatal(0, "%s: source field is %d bits wide but destination is "
> -                  "%d bits wide", full_s, src.n_bits, dst.n_bits);
> +                  "%d bits wide", full_s,
> +                  move->src.n_bits, move->dst.n_bits);
>      }
> -
> -    ofputil_init_NXAST_REG_MOVE(move);
> -    move->n_bits = htons(src.n_bits);
> -    move->src_ofs = htons(src.ofs);
> -    move->dst_ofs = htons(dst.ofs);
> -    move->src = htonl(src.field->nxm_header);
> -    move->dst = htonl(dst.field->nxm_header);
>  }
>
>  void
> -nxm_parse_reg_load(struct nx_action_reg_load *load, const char *s)
> +nxm_parse_reg_load(struct ofpact_reg_load *load, const char *s)
>  {
>      const char *full_s = s;
> -    struct mf_subfield dst;
> -    uint64_t value;
>
> -    value = strtoull(s, (char **) &s, 0);
> +    load->value = strtoull(s, (char **) &s, 0);
>      if (strncmp(s, "->", 2)) {
>          ovs_fatal(0, "%s: missing `->' following value", full_s);
>      }
>      s += 2;
> -    s = mf_parse_subfield(&dst, s);
> +    s = mf_parse_subfield(&load->dst, s);
>      if (*s != '\0') {
>          ovs_fatal(0, "%s: trailing garbage following destination", full_s);
>      }
>
> -    if (dst.n_bits < 64 && (value >> dst.n_bits) != 0) {
> -        ovs_fatal(0, "%s: value %"PRIu64" does not fit into %u bits",
> -                  full_s, value, dst.n_bits);
> +    if (load->dst.n_bits < 64 && (load->value >> load->dst.n_bits) != 0) {
> +        ovs_fatal(0, "%s: value %"PRIu64" does not fit into %d bits",
> +                  full_s, load->value, load->dst.n_bits);
>      }
> -
> -    ofputil_init_NXAST_REG_LOAD(load);
> -    load->ofs_nbits = nxm_encode_ofs_nbits(dst.ofs, dst.n_bits);
> -    load->dst = htonl(dst.field->nxm_header);
> -    load->value = htonll(value);
>  }
>
>  /* nxm_format_reg_move(), nxm_format_reg_load(). */
>
>  void
> -nxm_format_reg_move(const struct nx_action_reg_move *move, struct ds *s)
> +nxm_format_reg_move(const struct ofpact_reg_move *move, struct ds *s)
>  {
> -    struct mf_subfield src, dst;
> -
> -    nxm_decode_discrete(&src, move->src, move->src_ofs, move->n_bits);
> -    nxm_decode_discrete(&dst, move->dst, move->dst_ofs, move->n_bits);
> -
>      ds_put_format(s, "move:");
> -    mf_format_subfield(&src, s);
> +    mf_format_subfield(&move->src, s);
>      ds_put_cstr(s, "->");
> -    mf_format_subfield(&dst, s);
> +    mf_format_subfield(&move->dst, s);
>  }
>
>  void
> -nxm_format_reg_load(const struct nx_action_reg_load *load, struct ds *s)
> +nxm_format_reg_load(const struct ofpact_reg_load *load, struct ds *s)
> +{
> +    ds_put_format(s, "load:%#"PRIx64"->", load->value);
> +    mf_format_subfield(&load->dst, s);
> +}
> +
> +enum ofperr
> +nxm_reg_move_from_openflow(const struct nx_action_reg_move *narm,
> +                           struct ofpbuf *ofpacts)
>  {
> -    struct mf_subfield dst;
> +    struct ofpact_reg_move *move;
>
> -    ds_put_format(s, "load:%#"PRIx64"->", ntohll(load->value));
> +    move = ofpact_put_REG_MOVE(ofpacts);
> +    move->src.field = mf_from_nxm_header(ntohl(narm->src));
> +    move->src.ofs = ntohs(narm->src_ofs);
> +    move->src.n_bits = ntohs(narm->n_bits);
> +    move->dst.field = mf_from_nxm_header(ntohl(narm->dst));
> +    move->dst.ofs = ntohs(narm->dst_ofs);
> +    move->dst.n_bits = ntohs(narm->n_bits);
>
> -    nxm_decode(&dst, load->dst, load->ofs_nbits);
> -    mf_format_subfield(&dst, s);
> +    return nxm_reg_move_check(move, NULL);
>  }
> -
> -/* nxm_check_reg_move(), nxm_check_reg_load(). */
>
>  enum ofperr
> -nxm_check_reg_move(const struct nx_action_reg_move *action,
> -                   const struct flow *flow)
> +nxm_reg_load_from_openflow(const struct nx_action_reg_load *narl,
> +                           struct ofpbuf *ofpacts)
>  {
> -    struct mf_subfield src;
> -    struct mf_subfield dst;
> -    int error;
> +    struct ofpact_reg_load *load;
>
> -    nxm_decode_discrete(&src, action->src, action->src_ofs, action->n_bits);
> -    error = mf_check_src(&src, flow);
> -    if (error) {
> -        return error;
> +    load = ofpact_put_REG_LOAD(ofpacts);
> +    load->dst.field = mf_from_nxm_header(ntohl(narl->dst));
> +    load->dst.ofs = nxm_decode_ofs(narl->ofs_nbits);
> +    load->dst.n_bits = nxm_decode_n_bits(narl->ofs_nbits);
> +    load->value = ntohll(narl->value);
> +
> +    /* Reject 'narl' if a bit numbered 'n_bits' or higher is set to 1 in
> +     * narl->value. */
> +    if (load->dst.n_bits < 64 && load->value >> load->dst.n_bits) {
> +        return OFPERR_OFPBAC_BAD_ARGUMENT;
>      }
>
> -    nxm_decode_discrete(&dst, action->dst, action->dst_ofs, action->n_bits);
> -    return mf_check_dst(&dst, flow);
> +    return nxm_reg_load_check(load, NULL);
>  }
> -
> +
>  enum ofperr
> -nxm_check_reg_load(const struct nx_action_reg_load *action,
> -                   const struct flow *flow)
> +nxm_reg_move_check(const struct ofpact_reg_move *move, const struct flow *flow)
>  {
> -    struct mf_subfield dst;
>      enum ofperr error;
>
> -    nxm_decode(&dst, action->dst, action->ofs_nbits);
> -    error = mf_check_dst(&dst, flow);
> +    error = mf_check_src(&move->src, flow);
>      if (error) {
>          return error;
>      }
>
> -    /* Reject 'action' if a bit numbered 'n_bits' or higher is set to 1 in
> -     * action->value. */
> -    if (dst.n_bits < 64 && ntohll(action->value) >> dst.n_bits) {
> -        return OFPERR_OFPBAC_BAD_ARGUMENT;
> -    }
> +    return mf_check_dst(&move->dst, NULL);
> +}
>
> -    return 0;
> +enum ofperr
> +nxm_reg_load_check(const struct ofpact_reg_load *load, const struct flow *flow)
> +{
> +    return mf_check_dst(&load->dst, flow);
> +}
> +
> +void
> +nxm_reg_move_to_openflow(const struct ofpact_reg_move *move,
> +                           struct ofpbuf *openflow)
> +{
> +    struct nx_action_reg_move *narm;
> +
> +    narm = ofputil_put_NXAST_REG_MOVE(openflow);
> +    narm->n_bits = htons(move->dst.n_bits);
> +    narm->src_ofs = htons(move->src.ofs);
> +    narm->dst_ofs = htons(move->dst.ofs);
> +    narm->src = htonl(move->src.field->nxm_header);
> +    narm->dst = htonl(move->dst.field->nxm_header);
> +}
> +
> +void
> +nxm_reg_load_to_openflow(const struct ofpact_reg_load *load,
> +                         struct ofpbuf *openflow)
> +{
> +    struct nx_action_reg_load *narl;
> +
> +    narl = ofputil_put_NXAST_REG_LOAD(openflow);
> +    narl->ofs_nbits = nxm_encode_ofs_nbits(load->dst.ofs, load->dst.n_bits);
> +    narl->dst = htonl(load->dst.field->nxm_header);
> +    narl->value = htonll(load->value);
>  }
>
>  /* nxm_execute_reg_move(), nxm_execute_reg_load(). */
>
>  void
> -nxm_execute_reg_move(const struct nx_action_reg_move *action,
> +nxm_execute_reg_move(const struct ofpact_reg_move *move,
>                       struct flow *flow)
>  {
> -    struct mf_subfield src, dst;
>      union mf_value src_value;
>      union mf_value dst_value;
>
> -    nxm_decode_discrete(&src, action->src, action->src_ofs, action->n_bits);
> -    nxm_decode_discrete(&dst, action->dst, action->dst_ofs, action->n_bits);
> -
> -    mf_get_value(dst.field, flow, &dst_value);
> -    mf_get_value(src.field, flow, &src_value);
> -    bitwise_copy(&src_value, src.field->n_bytes, src.ofs,
> -                 &dst_value, dst.field->n_bytes, dst.ofs,
> -                 src.n_bits);
> -    mf_set_flow_value(dst.field, &dst_value, flow);
> +    mf_get_value(move->dst.field, flow, &dst_value);
> +    mf_get_value(move->src.field, flow, &src_value);
> +    bitwise_copy(&src_value, move->src.field->n_bytes, move->src.ofs,
> +                 &dst_value, move->dst.field->n_bytes, move->dst.ofs,
> +                 move->src.n_bits);
> +    mf_set_flow_value(move->dst.field, &dst_value, flow);
>  }
>
>  void
> -nxm_execute_reg_load(const struct nx_action_reg_load *action,
> -                     struct flow *flow)
> +nxm_execute_reg_load(const struct ofpact_reg_load *load, struct flow *flow)
>  {
> -    struct mf_subfield dst;
> -
> -    nxm_decode(&dst, action->dst, action->ofs_nbits);
> -    mf_set_subfield_value(&dst, ntohll(action->value), flow);
> +    nxm_reg_load(&load->dst, load->value, flow);
>  }
>
> -/* Initializes 'sf->field' with the field corresponding to the given NXM
> - * 'header' and 'sf->ofs' and 'sf->n_bits' decoded from 'ofs_nbits' with
> - * nxm_decode_ofs() and nxm_decode_n_bits(), respectively.
> - *
> - * Afterward, 'sf' might be invalid in a few different ways:
> - *
> - *   - 'sf->field' will be NULL if 'header' is unknown.
> - *
> - *   - 'sf->ofs' and 'sf->n_bits' might exceed the width of sf->field.
> - *
> - * The caller should call mf_check_src() or mf_check_dst() to check for these
> - * problems. */
>  void
> -nxm_decode(struct mf_subfield *sf, ovs_be32 header, ovs_be16 ofs_nbits)
> +nxm_reg_load(const struct mf_subfield *dst, uint64_t src_data,
> +             struct flow *flow)
>  {
> -    sf->field = mf_from_nxm_header(ntohl(header));
> -    sf->ofs = nxm_decode_ofs(ofs_nbits);
> -    sf->n_bits = nxm_decode_n_bits(ofs_nbits);
> +    union mf_value dst_value;
> +    union mf_value src_value;
> +
> +    mf_get_value(dst->field, flow, &dst_value);
> +    src_value.be64 = htonll(src_data);
> +    bitwise_copy(&src_value, sizeof src_value.be64, 0,
> +                 &dst_value, dst->field->n_bytes, dst->ofs,
> +                 dst->n_bits);
> +    mf_set_flow_value(dst->field, &dst_value, flow);
>  }
>
>  /* Initializes 'sf->field' with the field corresponding to the given NXM
> diff --git a/lib/nx-match.h b/lib/nx-match.h
> index c814275..c57e240 100644
> --- a/lib/nx-match.h
> +++ b/lib/nx-match.h
> @@ -21,6 +21,7 @@
>  #include <sys/types.h>
>  #include <netinet/in.h>
>  #include "flow.h"
> +#include "ofp-errors.h"
>  #include "openvswitch/types.h"
>  #include "ofp-errors.h"
>
> @@ -28,6 +29,8 @@ struct cls_rule;
>  struct ds;
>  struct flow;
>  struct mf_subfield;
> +struct ofpact_reg_move;
> +struct ofpact_reg_load;
>  struct ofpbuf;
>  struct nx_action_reg_load;
>  struct nx_action_reg_move;
> @@ -49,19 +52,31 @@ int nx_put_match(struct ofpbuf *, bool oxm, const struct cls_rule *,
>  char *nx_match_to_string(const uint8_t *, unsigned int match_len);
>  int nx_match_from_string(const char *, struct ofpbuf *);
>
> -void nxm_parse_reg_move(struct nx_action_reg_move *, const char *);
> -void nxm_parse_reg_load(struct nx_action_reg_load *, const char *);
> +void nxm_parse_reg_move(struct ofpact_reg_move *, const char *);
> +void nxm_parse_reg_load(struct ofpact_reg_load *, const char *);
> +
> +void nxm_format_reg_move(const struct ofpact_reg_move *, struct ds *);
> +void nxm_format_reg_load(const struct ofpact_reg_load *, struct ds *);
>
> -void nxm_format_reg_move(const struct nx_action_reg_move *, struct ds *);
> -void nxm_format_reg_load(const struct nx_action_reg_load *, struct ds *);
> +enum ofperr nxm_reg_move_from_openflow(const struct nx_action_reg_move *,
> +                                       struct ofpbuf *ofpacts);
> +enum ofperr nxm_reg_load_from_openflow(const struct nx_action_reg_load *,
> +                                       struct ofpbuf *ofpacts);
>
> -enum ofperr nxm_check_reg_move(const struct nx_action_reg_move *,
> +enum ofperr nxm_reg_move_check(const struct ofpact_reg_move *,
>                                 const struct flow *);
> -enum ofperr nxm_check_reg_load(const struct nx_action_reg_load *,
> +enum ofperr nxm_reg_load_check(const struct ofpact_reg_load *,
>                                 const struct flow *);
>
> -void nxm_execute_reg_move(const struct nx_action_reg_move *, struct flow *);
> -void nxm_execute_reg_load(const struct nx_action_reg_load *, struct flow *);
> +void nxm_reg_move_to_openflow(const struct ofpact_reg_move *,
> +                              struct ofpbuf *openflow);
> +void nxm_reg_load_to_openflow(const struct ofpact_reg_load *,
> +                              struct ofpbuf *openflow);
> +
> +void nxm_execute_reg_move(const struct ofpact_reg_move *, struct flow *);
> +void nxm_execute_reg_load(const struct ofpact_reg_load *, struct flow *);
> +void nxm_reg_load(const struct mf_subfield *, uint64_t src_data,
> +                  struct flow *);
>
>  int nxm_field_bytes(uint32_t header);
>  int nxm_field_bits(uint32_t header);
> diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
> new file mode 100644
> index 0000000..cf8ea95
> --- /dev/null
> +++ b/lib/ofp-actions.c
> @@ -0,0 +1,1213 @@
> +/*
> + * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks.
> + *
> + * Licensed under the Apache License, Version 2.0 (the "License");
> + * you may not use this file except in compliance with the License.
> + * You may obtain a copy of the License at:
> + *
> + *     http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing, software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> + * See the License for the specific language governing permissions and
> + * limitations under the License.
> + */
> +
> +#include <config.h>
> +#include "ofp-actions.h"
> +#include "autopath.h"
> +#include "bundle.h"
> +#include "byte-order.h"
> +#include "compiler.h"
> +#include "dynamic-string.h"
> +#include "learn.h"
> +#include "meta-flow.h"
> +#include "multipath.h"
> +#include "nx-match.h"
> +#include "ofp-util.h"
> +#include "ofpbuf.h"
> +#include "vlog.h"
> +
> +VLOG_DEFINE_THIS_MODULE(ofp_actions);
> +
> +static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> +
> +/* Converting OpenFlow 1.0 to ofpacts. */
> +
> +static enum ofperr
> +output_from_openflow10(const struct ofp_action_output *oao,
> +                       struct ofpbuf *out)
> +{
> +    struct ofpact_output *output;
> +
> +    output = ofpact_put_OUTPUT(out);
> +    output->port = ntohs(oao->port);
> +    output->max_len = ntohs(oao->max_len);
> +
> +    return ofputil_check_output_port(output->port, OFPP_MAX);
> +}
> +
> +static enum ofperr
> +enqueue_from_openflow10(const struct ofp_action_enqueue *oae,
> +                        struct ofpbuf *out)
> +{
> +    struct ofpact_enqueue *enqueue;
> +
> +    enqueue = ofpact_put_ENQUEUE(out);
> +    enqueue->port = ntohs(oae->port);
> +    enqueue->queue = ntohl(oae->queue_id);
> +    if (enqueue->port >= OFPP_MAX && enqueue->port != OFPP_IN_PORT
> +        && enqueue->port != OFPP_LOCAL) {
> +        return OFPERR_OFPBAC_BAD_OUT_PORT;
> +    }
> +    return 0;
> +}
> +
> +static void
> +resubmit_from_openflow(const struct nx_action_resubmit *nar,
> +                       struct ofpbuf *out)
> +{
> +    struct ofpact_resubmit *resubmit;
> +
> +    resubmit = ofpact_put_RESUBMIT(out);
> +    resubmit->ofpact.compat = OFPUTIL_NXAST_RESUBMIT;
> +    resubmit->in_port = ntohs(nar->in_port);
> +    resubmit->table_id = 0xff;
> +}
> +
> +static enum ofperr
> +resubmit_table_from_openflow(const struct nx_action_resubmit *nar,
> +                             struct ofpbuf *out)
> +{
> +    struct ofpact_resubmit *resubmit;
> +
> +    if (nar->pad[0] || nar->pad[1] || nar->pad[2]) {
> +        return OFPERR_OFPBAC_BAD_ARGUMENT;
> +    }
> +
> +    resubmit = ofpact_put_RESUBMIT(out);
> +    resubmit->ofpact.compat = OFPUTIL_NXAST_RESUBMIT_TABLE;
> +    resubmit->in_port = ntohs(nar->in_port);
> +    resubmit->table_id = nar->table;
> +    return 0;
> +}
> +
> +static enum ofperr
> +output_reg_from_openflow(const struct nx_action_output_reg *naor,
> +                         struct ofpbuf *out)
> +{
> +    struct ofpact_output_reg *output_reg;
> +
> +    if (!is_all_zeros(naor->zero, sizeof naor->zero)) {
> +        return OFPERR_OFPBAC_BAD_ARGUMENT;
> +    }
> +
> +    output_reg = ofpact_put_OUTPUT_REG(out);
> +    output_reg->src.field = mf_from_nxm_header(ntohl(naor->src));
> +    output_reg->src.ofs = nxm_decode_ofs(naor->ofs_nbits);
> +    output_reg->src.n_bits = nxm_decode_n_bits(naor->ofs_nbits);
> +    output_reg->max_len = ntohs(naor->max_len);
> +
> +    return mf_check_src(&output_reg->src, NULL);
> +}
> +
> +static void
> +fin_timeout_from_openflow(const struct nx_action_fin_timeout *naft,
> +                          struct ofpbuf *out)
> +{
> +    struct ofpact_fin_timeout *oft;
> +
> +    oft = ofpact_put_FIN_TIMEOUT(out);
> +    oft->fin_idle_timeout = ntohs(naft->fin_idle_timeout);
> +    oft->fin_hard_timeout = ntohs(naft->fin_hard_timeout);
> +}
> +
> +static void
> +controller_from_openflow(const struct nx_action_controller *nac,
> +                         struct ofpbuf *out)
> +{
> +    struct ofpact_controller *oc;
> +
> +    oc = ofpact_put_CONTROLLER(out);
> +    oc->max_len = ntohs(nac->max_len);
> +    oc->controller_id = ntohs(nac->controller_id);
> +    oc->reason = nac->reason;
> +}
> +
> +static void
> +note_from_openflow(const struct nx_action_note *nan, struct ofpbuf *out)
> +{
> +    struct ofpact_note *note;
> +    unsigned int length;
> +
> +    length = ntohs(nan->len) - offsetof(struct nx_action_note, note);
> +    note = ofpact_put(out, OFPACT_NOTE,
> +                      offsetof(struct ofpact_note, data) + length);
> +    note->length = length;
> +    memcpy(note->data, nan->note, length);
> +}
> +
> +static enum ofperr
> +decode_nxast_action(const union ofp_action *a, enum ofputil_action_code *code)
> +{
> +    const struct nx_action_header *nah = (const struct nx_action_header *) a;
> +    uint16_t len = ntohs(a->header.len);
> +
> +    if (len < sizeof(struct nx_action_header)) {
> +        return OFPERR_OFPBAC_BAD_LEN;
> +    } else if (a->vendor.vendor != CONSTANT_HTONL(NX_VENDOR_ID)) {
> +        return OFPERR_OFPBAC_BAD_VENDOR;
> +    }
> +
> +    switch (nah->subtype) {
> +#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME)    \
> +        case CONSTANT_HTONS(ENUM):                      \
> +            if (EXTENSIBLE                              \
> +                ? len >= sizeof(struct STRUCT)          \
> +                : len == sizeof(struct STRUCT)) {       \
> +                *code = OFPUTIL_##ENUM;                 \
> +                return 0;                               \
> +            } else {                                    \
> +                return OFPERR_OFPBAC_BAD_LEN;           \
> +            }                                           \
> +            NOT_REACHED();
> +#include "ofp-util.def"
> +
> +    case CONSTANT_HTONS(NXAST_SNAT__OBSOLETE):
> +    case CONSTANT_HTONS(NXAST_DROP_SPOOFED_ARP__OBSOLETE):
> +    default:
> +        return OFPERR_OFPBAC_BAD_TYPE;
> +    }
> +}
> +
> +/* Parses 'a' to determine its type.  On success stores the correct type into
> + * '*code' and returns 0.  On failure returns an OFPERR_* error code and
> + * '*code' is indeterminate.
> + *
> + * The caller must have already verified that 'a''s length is potentially
> + * correct (that is, a->header.len is nonzero and a multiple of sizeof(union
> + * ofp_action) and no longer than the amount of space allocated to 'a').
> + *
> + * This function verifies that 'a''s length is correct for the type of action
> + * that it represents. */
> +static enum ofperr
> +decode_openflow10_action(const union ofp_action *a,
> +                         enum ofputil_action_code *code)
> +{
> +    switch (a->type) {
> +    case CONSTANT_HTONS(OFPAT10_VENDOR):
> +        return decode_nxast_action(a, code);
> +
> +#define OFPAT10_ACTION(ENUM, STRUCT, NAME)                          \
> +        case CONSTANT_HTONS(ENUM):                                  \
> +            if (a->header.len == htons(sizeof(struct STRUCT))) {    \
> +                *code = OFPUTIL_##ENUM;                             \
> +                return 0;                                           \
> +            } else {                                                \
> +                return OFPERR_OFPBAC_BAD_LEN;                       \
> +            }                                                       \
> +            break;
> +#include "ofp-util.def"
> +
> +    default:
> +        return OFPERR_OFPBAC_BAD_TYPE;
> +    }
> +}
> +
> +static enum ofperr
> +ofpact_from_openflow10__(const union ofp_action *a, struct ofpbuf *out)
> +{
> +    const struct nx_action_resubmit *nar;
> +    const struct nx_action_set_tunnel *nast;
> +    const struct nx_action_set_queue *nasq;
> +    const struct nx_action_note *nan;
> +    const struct nx_action_set_tunnel64 *nast64;
> +    struct ofpact_tunnel *tunnel;
> +    enum ofputil_action_code code;
> +    enum ofperr error;
> +
> +    error = decode_openflow10_action(a, &code);
> +    if (error) {
> +        return error;
> +    }
> +
> +    switch (code) {
> +    case OFPUTIL_ACTION_INVALID:
> +        NOT_REACHED();
> +
> +    case OFPUTIL_OFPAT10_OUTPUT:
> +        return output_from_openflow10((const struct ofp_action_output *) a,
> +                                      out);
> +
> +    case OFPUTIL_OFPAT10_SET_VLAN_VID:
> +        if (a->vlan_vid.vlan_vid & ~htons(0xfff)) {
> +            return OFPERR_OFPBAC_BAD_ARGUMENT;
> +        }
> +        ofpact_put_SET_VLAN_VID(out)->vlan_vid = ntohs(a->vlan_vid.vlan_vid);
> +        break;
> +
> +    case OFPUTIL_OFPAT10_SET_VLAN_PCP:
> +        if (a->vlan_pcp.vlan_pcp & ~7) {
> +            return OFPERR_OFPBAC_BAD_ARGUMENT;
> +        }
> +        ofpact_put_SET_VLAN_PCP(out)->vlan_pcp = a->vlan_pcp.vlan_pcp;
> +        break;
> +
> +    case OFPUTIL_OFPAT10_STRIP_VLAN:
> +        ofpact_put_STRIP_VLAN(out);
> +        break;
> +
> +    case OFPUTIL_OFPAT10_SET_DL_SRC:
> +        memcpy(ofpact_put_SET_ETH_SRC(out)->mac,
> +               ((const struct ofp_action_dl_addr *) a)->dl_addr, ETH_ADDR_LEN);
> +        break;
> +
> +    case OFPUTIL_OFPAT10_SET_DL_DST:
> +        memcpy(ofpact_put_SET_ETH_DST(out)->mac,
> +               ((const struct ofp_action_dl_addr *) a)->dl_addr, ETH_ADDR_LEN);
> +        break;
> +
> +    case OFPUTIL_OFPAT10_SET_NW_SRC:
> +        ofpact_put_SET_IPV4_SRC(out)->ipv4 = a->nw_addr.nw_addr;
> +        break;
> +
> +    case OFPUTIL_OFPAT10_SET_NW_DST:
> +        ofpact_put_SET_IPV4_DST(out)->ipv4 = a->nw_addr.nw_addr;
> +        break;
> +
> +    case OFPUTIL_OFPAT10_SET_NW_TOS:
> +        if (a->nw_tos.nw_tos & ~IP_DSCP_MASK) {
> +            return OFPERR_OFPBAC_BAD_ARGUMENT;
> +        }
> +        ofpact_put_SET_IPV4_DSCP(out)->dscp = a->nw_tos.nw_tos;
> +        break;
> +
> +    case OFPUTIL_OFPAT10_SET_TP_SRC:
> +        ofpact_put_SET_L4_SRC_PORT(out)->port = ntohs(a->tp_port.tp_port);
> +        break;
> +
> +    case OFPUTIL_OFPAT10_SET_TP_DST:
> +        ofpact_put_SET_L4_DST_PORT(out)->port = ntohs(a->tp_port.tp_port);
> +
> +        break;
> +
> +    case OFPUTIL_OFPAT10_ENQUEUE:
> +        error = enqueue_from_openflow10((const struct ofp_action_enqueue *) a,
> +                                        out);
> +        break;
> +
> +    case OFPUTIL_NXAST_RESUBMIT:
> +        resubmit_from_openflow((const struct nx_action_resubmit *) a, out);
> +        break;
> +
> +    case OFPUTIL_NXAST_SET_TUNNEL:
> +        nast = (const struct nx_action_set_tunnel *) a;
> +        tunnel = ofpact_put_SET_TUNNEL(out);
> +        tunnel->ofpact.compat = code;
> +        tunnel->tun_id = ntohl(nast->tun_id);
> +        break;
> +
> +    case OFPUTIL_NXAST_SET_QUEUE:
> +        nasq = (const struct nx_action_set_queue *) a;
> +        ofpact_put_SET_QUEUE(out)->queue_id = ntohl(nasq->queue_id);
> +        break;
> +
> +    case OFPUTIL_NXAST_POP_QUEUE:
> +        ofpact_put_POP_QUEUE(out);
> +        break;
> +
> +    case OFPUTIL_NXAST_REG_MOVE:
> +        error = nxm_reg_move_from_openflow(
> +            (const struct nx_action_reg_move *) a, out);
> +        break;
> +
> +    case OFPUTIL_NXAST_REG_LOAD:
> +        error = nxm_reg_load_from_openflow(
> +            (const struct nx_action_reg_load *) a, out);
> +        break;
> +
> +    case OFPUTIL_NXAST_NOTE:
> +        nan = (const struct nx_action_note *) a;
> +        note_from_openflow(nan, out);
> +        break;
> +
> +    case OFPUTIL_NXAST_SET_TUNNEL64:
> +        nast64 = (const struct nx_action_set_tunnel64 *) a;
> +        tunnel = ofpact_put_SET_TUNNEL(out);
> +        tunnel->ofpact.compat = code;
> +        tunnel->tun_id = ntohll(nast64->tun_id);
> +        break;
> +
> +    case OFPUTIL_NXAST_MULTIPATH:
> +        error = multipath_from_openflow((const struct nx_action_multipath *) a,
> +                                        ofpact_put_MULTIPATH(out));
> +        break;
> +
> +    case OFPUTIL_NXAST_AUTOPATH:
> +        error = autopath_from_openflow((const struct nx_action_autopath *) a,
> +                                       ofpact_put_AUTOPATH(out));
> +        break;
> +
> +    case OFPUTIL_NXAST_BUNDLE:
> +    case OFPUTIL_NXAST_BUNDLE_LOAD:
> +        error = bundle_from_openflow((const struct nx_action_bundle *) a, out);
> +        break;
> +
> +    case OFPUTIL_NXAST_OUTPUT_REG:
> +        error = output_reg_from_openflow(
> +            (const struct nx_action_output_reg *) a, out);
> +        break;
> +
> +    case OFPUTIL_NXAST_RESUBMIT_TABLE:
> +        nar = (const struct nx_action_resubmit *) a;
> +        error = resubmit_table_from_openflow(nar, out);
> +        break;
> +
> +    case OFPUTIL_NXAST_LEARN:
> +        error = learn_from_openflow((const struct nx_action_learn *) a, out);
> +        break;
> +
> +    case OFPUTIL_NXAST_EXIT:
> +        ofpact_put_EXIT(out);
> +        break;
> +
> +    case OFPUTIL_NXAST_DEC_TTL:
> +        ofpact_put_DEC_TTL(out);
> +        break;
> +
> +    case OFPUTIL_NXAST_FIN_TIMEOUT:
> +        fin_timeout_from_openflow(
> +            (const struct nx_action_fin_timeout *) a, out);
> +        break;
> +
> +    case OFPUTIL_NXAST_CONTROLLER:
> +        controller_from_openflow((const struct nx_action_controller *) a, out);
> +        break;
> +    }
> +
> +    return error;
> +}
> +
> +static inline union ofp_action *
> +action_next(const union ofp_action *a)
> +{
> +    return ((union ofp_action *) (void *)
> +            ((uint8_t *) a + ntohs(a->header.len)));
> +}
> +
> +static inline bool
> +action_is_valid(const union ofp_action *a, size_t n_actions)
> +{
> +    uint16_t len = ntohs(a->header.len);
> +    return (!(len % OFP_ACTION_ALIGN)
> +            && len >= sizeof *a
> +            && len / sizeof *a <= n_actions);
> +}
> +
> +/* This macro is careful to check for actions with bad lengths. */
> +#define ACTION_FOR_EACH(ITER, LEFT, ACTIONS, N_ACTIONS)                 \
> +    for ((ITER) = (ACTIONS), (LEFT) = (N_ACTIONS);                      \
> +         (LEFT) > 0 && action_is_valid(ITER, LEFT);                     \
> +         ((LEFT) -= ntohs((ITER)->header.len) / sizeof(union ofp_action), \
> +          (ITER) = action_next(ITER)))
> +
> +static enum ofperr
> +ofpact_from_openflow10(const union ofp_action *in, size_t n_in,
> +                       struct ofpbuf *out)
> +{
> +    const union ofp_action *a;
> +    size_t left;
> +
> +    ACTION_FOR_EACH (a, left, in, n_in) {
> +        enum ofperr error = ofpact_from_openflow10__(a, out);
> +        if (error) {
> +            VLOG_WARN_RL(&rl, "bad action at offset %td (%s)",
> +                         (a - in) * sizeof *a, ofperr_get_name(error));
> +            return error;
> +        }
> +    }
> +    if (left) {
> +        VLOG_WARN_RL(&rl, "bad action format at offset %zu",
> +                     (n_in - left) * sizeof *a);
> +        return OFPERR_OFPBAC_BAD_LEN;
> +    }
> +
> +    ofpact_put_END(out);
> +
> +    return 0;
> +}
> +
> +/* Attempts to convert 'actions_len' bytes of OpenFlow actions from the front
> + * of 'openflow' into ofpacts.  On success, replaces any existing content in
> + * 'ofpacts' by the converted ofpacts; on failure, clears 'ofpacts'.  Returns 0
> + * if successful, otherwise an OpenFlow error.
> + *
> + * This function does not check that the actions are valid in a given context.
> + * The caller should do so, with ofpacts_check(). */
> +enum ofperr
> +ofpacts_pull_openflow(struct ofpbuf *openflow, unsigned int actions_len,
> +                      struct ofpbuf *ofpacts)
> +{
> +    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> +    const union ofp_action *actions;
> +    enum ofperr error;
> +
> +    ofpbuf_clear(ofpacts);
> +
> +    if (actions_len % OFP_ACTION_ALIGN != 0) {
> +        VLOG_WARN_RL(&rl, "OpenFlow message actions length %u is not a "
> +                     "multiple of %d", actions_len, OFP_ACTION_ALIGN);
> +        return OFPERR_OFPBRC_BAD_LEN;
> +    }
> +
> +    actions = ofpbuf_try_pull(openflow, actions_len);
> +    if (actions == NULL) {
> +        VLOG_WARN_RL(&rl, "OpenFlow message actions length %u exceeds "
> +                     "remaining message length (%zu)",
> +                     actions_len, openflow->size);
> +        return OFPERR_OFPBRC_BAD_LEN;
> +    }
> +
> +    error = ofpact_from_openflow10(actions, actions_len / OFP_ACTION_ALIGN,
> +                                   ofpacts);
> +    if (error) {
> +        ofpbuf_clear(ofpacts);
> +    }
> +    return 0;
> +}
> +
> +static enum ofperr
> +ofpact_check__(const struct ofpact *a, const struct flow *flow, int max_ports)
> +{
> +    const struct ofpact_enqueue *enqueue;
> +
> +    switch (a->type) {
> +    case OFPACT_END:
> +        return 0;
> +
> +    case OFPACT_OUTPUT:
> +        return ofputil_check_output_port(ofpact_get_OUTPUT(a)->port,
> +                                         max_ports);
> +
> +    case OFPACT_CONTROLLER:
> +        return 0;
> +
> +    case OFPACT_ENQUEUE:
> +        enqueue = ofpact_get_ENQUEUE(a);
> +        if (enqueue->port >= max_ports && enqueue->port != OFPP_IN_PORT
> +            && enqueue->port != OFPP_LOCAL) {
> +            return OFPERR_OFPBAC_BAD_OUT_PORT;
> +        }
> +        return 0;
> +
> +    case OFPACT_OUTPUT_REG:
> +        return mf_check_src(&ofpact_get_OUTPUT_REG(a)->src, flow);
> +
> +    case OFPACT_BUNDLE:
> +        return bundle_check(ofpact_get_BUNDLE(a), max_ports, flow);
> +
> +    case OFPACT_SET_VLAN_VID:
> +    case OFPACT_SET_VLAN_PCP:
> +    case OFPACT_STRIP_VLAN:
> +    case OFPACT_SET_ETH_SRC:
> +    case OFPACT_SET_ETH_DST:
> +    case OFPACT_SET_IPV4_SRC:
> +    case OFPACT_SET_IPV4_DST:
> +    case OFPACT_SET_IPV4_DSCP:
> +    case OFPACT_SET_L4_SRC_PORT:
> +    case OFPACT_SET_L4_DST_PORT:
> +        return 0;
> +
> +    case OFPACT_REG_MOVE:
> +        return nxm_reg_move_check(ofpact_get_REG_MOVE(a), flow);
> +
> +    case OFPACT_REG_LOAD:
> +        return nxm_reg_load_check(ofpact_get_REG_LOAD(a), flow);
> +
> +    case OFPACT_DEC_TTL:
> +    case OFPACT_SET_TUNNEL:
> +    case OFPACT_SET_QUEUE:
> +    case OFPACT_POP_QUEUE:
> +    case OFPACT_FIN_TIMEOUT:
> +    case OFPACT_RESUBMIT:
> +        return 0;
> +
> +    case OFPACT_LEARN:
> +        return learn_check(ofpact_get_LEARN(a), flow);
> +
> +    case OFPACT_MULTIPATH:
> +        return multipath_check(ofpact_get_MULTIPATH(a), flow);
> +
> +    case OFPACT_AUTOPATH:
> +        return autopath_check(ofpact_get_AUTOPATH(a), flow);
> +
> +    case OFPACT_NOTE:
> +    case OFPACT_EXIT:
> +        return 0;
> +
> +    default:
> +        NOT_REACHED();
> +    }
> +}
> +
> +/* Checks that the actions in 'ofpacts' (terminated by OFPACT_END) are
> + * appropriate for a packet with the prerequisites satisfied by 'flow' in a
> + * switch with no more than 'max_ports' ports. */
> +enum ofperr
> +ofpacts_check(const struct ofpact ofpacts[],
> +              const struct flow *flow, int max_ports)
> +{
> +    const struct ofpact *a;
> +
> +    OFPACT_FOR_EACH (a, ofpacts) {
> +        enum ofperr error = ofpact_check__(a, flow, max_ports);
> +        if (error) {
> +            return error;
> +        }
> +    }
> +
> +    return 0;
> +}
> +
> +/* Converting ofpacts to Nicira OpenFlow extensions. */
> +
> +static void
> +ofpact_output_reg_to_nxast(const struct ofpact_output_reg *output_reg,
> +                                struct ofpbuf *out)
> +{
> +    struct nx_action_output_reg *naor = ofputil_put_NXAST_OUTPUT_REG(out);
> +
> +    naor->ofs_nbits = nxm_encode_ofs_nbits(output_reg->src.ofs,
> +                                           output_reg->src.n_bits);
> +    naor->src = htonl(output_reg->src.field->nxm_header);
> +    naor->max_len = htons(output_reg->max_len);
> +}
> +
> +static void
> +ofpact_resubmit_to_nxast(const struct ofpact_resubmit *resubmit,
> +                         struct ofpbuf *out)
> +{
> +    struct nx_action_resubmit *nar;
> +
> +    if (resubmit->table_id == 0xff
> +        && resubmit->ofpact.compat != OFPUTIL_NXAST_RESUBMIT_TABLE) {
> +        nar = ofputil_put_NXAST_RESUBMIT(out);
> +    } else {
> +        nar = ofputil_put_NXAST_RESUBMIT_TABLE(out);
> +        nar->table = resubmit->table_id;
> +    }
> +    nar->in_port = htons(resubmit->in_port);
> +}
> +
> +static void
> +ofpact_set_tunnel_to_nxast(const struct ofpact_tunnel *tunnel,
> +                           struct ofpbuf *out)
> +{
> +    uint64_t tun_id = tunnel->tun_id;
> +
> +    if (tun_id <= UINT32_MAX
> +        && tunnel->ofpact.compat != OFPUTIL_NXAST_SET_TUNNEL64) {
> +        ofputil_put_NXAST_SET_TUNNEL(out)->tun_id = htonl(tun_id);
> +    } else {
> +        ofputil_put_NXAST_SET_TUNNEL64(out)->tun_id = htonll(tun_id);
> +    }
> +}
> +
> +static void
> +ofpact_note_to_nxast(const struct ofpact_note *note, struct ofpbuf *out)
> +{
> +    size_t start_ofs = out->size;
> +    struct nx_action_note *nan;
> +    unsigned int remainder;
> +    unsigned int len;
> +
> +    nan = ofputil_put_NXAST_NOTE(out);
> +    out->size -= sizeof nan->note;
> +
> +    ofpbuf_put(out, note->data, note->length);
> +
> +    len = out->size - start_ofs;
> +    remainder = len % OFP_ACTION_ALIGN;
> +    if (remainder) {
> +        ofpbuf_put_zeros(out, OFP_ACTION_ALIGN - remainder);
> +    }
> +    nan = (struct nx_action_note *)((char *)out->data + start_ofs);
> +    nan->len = htons(out->size - start_ofs);
> +}
> +
> +static void
> +ofpact_controller_to_nxast(const struct ofpact_controller *oc,
> +                           struct ofpbuf *out)
> +{
> +    struct nx_action_controller *nac;
> +
> +    nac = ofputil_put_NXAST_CONTROLLER(out);
> +    nac->max_len = htons(oc->max_len);
> +    nac->controller_id = htons(oc->controller_id);
> +    nac->reason = oc->reason;
> +}
> +
> +static void
> +ofpact_fin_timeout_to_nxast(const struct ofpact_fin_timeout *fin_timeout,
> +                            struct ofpbuf *out)
> +{
> +    struct nx_action_fin_timeout *naft = ofputil_put_NXAST_FIN_TIMEOUT(out);
> +    naft->fin_idle_timeout = htons(fin_timeout->fin_idle_timeout);
> +    naft->fin_hard_timeout = htons(fin_timeout->fin_hard_timeout);
> +}
> +
> +static void
> +ofpact_to_nxast(const struct ofpact *a, struct ofpbuf *out)
> +{
> +    switch (a->type) {
> +    case OFPACT_CONTROLLER:
> +        ofpact_controller_to_nxast(ofpact_get_CONTROLLER(a), out);
> +        break;
> +
> +    case OFPACT_OUTPUT_REG:
> +        ofpact_output_reg_to_nxast(ofpact_get_OUTPUT_REG(a), out);
> +        break;
> +
> +    case OFPACT_BUNDLE:
> +        bundle_to_openflow(ofpact_get_BUNDLE(a), out);
> +        break;
> +
> +    case OFPACT_REG_MOVE:
> +        nxm_reg_move_to_openflow(ofpact_get_REG_MOVE(a), out);
> +        break;
> +
> +    case OFPACT_REG_LOAD:
> +        nxm_reg_load_to_openflow(ofpact_get_REG_LOAD(a), out);
> +        break;
> +
> +    case OFPACT_DEC_TTL:
> +        ofputil_put_NXAST_DEC_TTL(out);
> +        break;
> +
> +    case OFPACT_SET_TUNNEL:
> +        ofpact_set_tunnel_to_nxast(ofpact_get_SET_TUNNEL(a), out);
> +        break;
> +
> +    case OFPACT_SET_QUEUE:
> +        ofputil_put_NXAST_SET_QUEUE(out)->queue_id
> +            = htonl(ofpact_get_SET_QUEUE(a)->queue_id);
> +        break;
> +
> +    case OFPACT_POP_QUEUE:
> +        ofputil_put_NXAST_POP_QUEUE(out);
> +        break;
> +
> +    case OFPACT_FIN_TIMEOUT:
> +        ofpact_fin_timeout_to_nxast(ofpact_get_FIN_TIMEOUT(a), out);
> +        break;
> +
> +    case OFPACT_RESUBMIT:
> +        ofpact_resubmit_to_nxast(ofpact_get_RESUBMIT(a), out);
> +        break;
> +
> +    case OFPACT_LEARN:
> +        learn_to_openflow(ofpact_get_LEARN(a), out);
> +        break;
> +
> +    case OFPACT_MULTIPATH:
> +        multipath_to_openflow(ofpact_get_MULTIPATH(a), out);
> +        break;
> +
> +    case OFPACT_AUTOPATH:
> +        autopath_to_openflow(ofpact_get_AUTOPATH(a), out);
> +        break;
> +
> +    case OFPACT_NOTE:
> +        ofpact_note_to_nxast(ofpact_get_NOTE(a), out);
> +        break;
> +
> +    case OFPACT_EXIT:
> +        ofputil_put_NXAST_EXIT(out);
> +        break;
> +
> +    case OFPACT_END:
> +    case OFPACT_OUTPUT:
> +    case OFPACT_ENQUEUE:
> +    case OFPACT_SET_VLAN_VID:
> +    case OFPACT_SET_VLAN_PCP:
> +    case OFPACT_STRIP_VLAN:
> +    case OFPACT_SET_ETH_SRC:
> +    case OFPACT_SET_ETH_DST:
> +    case OFPACT_SET_IPV4_SRC:
> +    case OFPACT_SET_IPV4_DST:
> +    case OFPACT_SET_IPV4_DSCP:
> +    case OFPACT_SET_L4_SRC_PORT:
> +    case OFPACT_SET_L4_DST_PORT:
> +        NOT_REACHED();
> +    }
> +}
> +
> +/* Converting ofpacts to OpenFlow 1.0. */
> +
> +static void
> +ofpact_output_to_openflow10(const struct ofpact_output *output,
> +                            struct ofpbuf *out)
> +{
> +    struct ofp_action_output *oao;
> +
> +    oao = ofputil_put_OFPAT10_OUTPUT(out);
> +    oao->port = htons(output->port);
> +    oao->max_len = htons(output->max_len);
> +}
> +
> +static void
> +ofpact_enqueue_to_openflow10(const struct ofpact_enqueue *enqueue,
> +                             struct ofpbuf *out)
> +{
> +    struct ofp_action_enqueue *oae;
> +
> +    oae = ofputil_put_OFPAT10_ENQUEUE(out);
> +    oae->port = htons(enqueue->port);
> +    oae->queue_id = htonl(enqueue->queue);
> +}
> +
> +static void
> +ofpact_to_openflow10(const struct ofpact *a, struct ofpbuf *out)
> +{
> +    switch (a->type) {
> +    case OFPACT_END:
> +        NOT_REACHED();
> +
> +    case OFPACT_OUTPUT:
> +        ofpact_output_to_openflow10(ofpact_get_OUTPUT(a), out);
> +        break;
> +
> +    case OFPACT_ENQUEUE:
> +        ofpact_enqueue_to_openflow10(ofpact_get_ENQUEUE(a), out);
> +        break;
> +
> +    case OFPACT_SET_VLAN_VID:
> +        ofputil_put_OFPAT10_SET_VLAN_VID(out)->vlan_vid
> +            = htons(ofpact_get_SET_VLAN_VID(a)->vlan_vid);
> +        break;
> +
> +    case OFPACT_SET_VLAN_PCP:
> +        ofputil_put_OFPAT10_SET_VLAN_PCP(out)->vlan_pcp
> +            = ofpact_get_SET_VLAN_PCP(a)->vlan_pcp;
> +        break;
> +
> +    case OFPACT_STRIP_VLAN:
> +        ofputil_put_OFPAT10_STRIP_VLAN(out);
> +        break;
> +
> +    case OFPACT_SET_ETH_SRC:
> +        memcpy(ofputil_put_OFPAT10_SET_DL_SRC(out)->dl_addr,
> +               ofpact_get_SET_ETH_SRC(a)->mac, ETH_ADDR_LEN);
> +        break;
> +
> +    case OFPACT_SET_ETH_DST:
> +        memcpy(ofputil_put_OFPAT10_SET_DL_DST(out)->dl_addr,
> +               ofpact_get_SET_ETH_DST(a)->mac, ETH_ADDR_LEN);
> +        break;
> +
> +    case OFPACT_SET_IPV4_SRC:
> +        ofputil_put_OFPAT10_SET_NW_SRC(out)->nw_addr
> +            = ofpact_get_SET_IPV4_SRC(a)->ipv4;
> +        break;
> +
> +    case OFPACT_SET_IPV4_DST:
> +        ofputil_put_OFPAT10_SET_NW_DST(out)->nw_addr
> +            = ofpact_get_SET_IPV4_DST(a)->ipv4;
> +        break;
> +
> +    case OFPACT_SET_IPV4_DSCP:
> +        ofputil_put_OFPAT10_SET_NW_TOS(out)->nw_tos
> +            = ofpact_get_SET_IPV4_DSCP(a)->dscp;
> +        break;
> +
> +    case OFPACT_SET_L4_SRC_PORT:
> +        ofputil_put_OFPAT10_SET_TP_SRC(out)->tp_port
> +            = htons(ofpact_get_SET_L4_SRC_PORT(a)->port);
> +        break;
> +
> +    case OFPACT_SET_L4_DST_PORT:
> +        ofputil_put_OFPAT10_SET_TP_DST(out)->tp_port
> +            = htons(ofpact_get_SET_L4_DST_PORT(a)->port);
> +        break;
> +
> +    case OFPACT_CONTROLLER:
> +    case OFPACT_OUTPUT_REG:
> +    case OFPACT_BUNDLE:
> +    case OFPACT_REG_MOVE:
> +    case OFPACT_REG_LOAD:
> +    case OFPACT_DEC_TTL:
> +    case OFPACT_SET_TUNNEL:
> +    case OFPACT_SET_QUEUE:
> +    case OFPACT_POP_QUEUE:
> +    case OFPACT_FIN_TIMEOUT:
> +    case OFPACT_RESUBMIT:
> +    case OFPACT_LEARN:
> +    case OFPACT_MULTIPATH:
> +    case OFPACT_AUTOPATH:
> +    case OFPACT_NOTE:
> +    case OFPACT_EXIT:
> +        ofpact_to_nxast(a, out);
> +        break;
> +    }
> +}
> +
> +/* Converts the ofpacts in 'ofpacts' (terminated by OFPACT_END) into OpenFlow
> + * actions in 'openflow', appending the actions to any existing data in
> + * 'openflow'. */
> +void
> +ofpacts_to_openflow(const struct ofpact ofpacts[], struct ofpbuf *openflow)
> +{
> +    const struct ofpact *a;
> +
> +    OFPACT_FOR_EACH (a, ofpacts) {
> +        ofpact_to_openflow10(a, openflow);
> +    }
> +}
> +
> +/* Returns true if 'action' outputs to 'port', false otherwise. */
> +static bool
> +ofpact_outputs_to_port(const struct ofpact *ofpact, uint16_t port)
> +{
> +    switch (ofpact->type) {
> +    case OFPACT_OUTPUT:
> +        return ofpact_get_OUTPUT(ofpact)->port == port;
> +    case OFPACT_ENQUEUE:
> +        return ofpact_get_ENQUEUE(ofpact)->port == port;
> +    case OFPACT_CONTROLLER:
> +        return port == OFPP_CONTROLLER;
> +
> +    case OFPACT_END:
> +    case OFPACT_OUTPUT_REG:
> +    case OFPACT_BUNDLE:
> +    case OFPACT_SET_VLAN_VID:
> +    case OFPACT_SET_VLAN_PCP:
> +    case OFPACT_STRIP_VLAN:
> +    case OFPACT_SET_ETH_SRC:
> +    case OFPACT_SET_ETH_DST:
> +    case OFPACT_SET_IPV4_SRC:
> +    case OFPACT_SET_IPV4_DST:
> +    case OFPACT_SET_IPV4_DSCP:
> +    case OFPACT_SET_L4_SRC_PORT:
> +    case OFPACT_SET_L4_DST_PORT:
> +    case OFPACT_REG_MOVE:
> +    case OFPACT_REG_LOAD:
> +    case OFPACT_DEC_TTL:
> +    case OFPACT_SET_TUNNEL:
> +    case OFPACT_SET_QUEUE:
> +    case OFPACT_POP_QUEUE:
> +    case OFPACT_FIN_TIMEOUT:
> +    case OFPACT_RESUBMIT:
> +    case OFPACT_LEARN:
> +    case OFPACT_MULTIPATH:
> +    case OFPACT_AUTOPATH:
> +    case OFPACT_NOTE:
> +    case OFPACT_EXIT:
> +    default:
> +        return false;
> +    }
> +}
> +
> +/* Returns true if any action in 'ofpacts' outputs to 'port', false
> + * otherwise. */
> +bool
> +ofpacts_output_to_port(const struct ofpact *ofpacts, uint16_t port)
> +{
> +    const struct ofpact *a;
> +
> +    OFPACT_FOR_EACH (a, ofpacts) {
> +        if (ofpact_outputs_to_port(a, port)) {
> +            return true;
> +        }
> +    }
> +
> +    return false;
> +}
> +
> +bool
> +ofpacts_equal(const struct ofpact *a, size_t a_len,
> +              const struct ofpact *b, size_t b_len)
> +{
> +    return a_len == b_len && !memcmp(a, b, a_len);
> +}
> +
> +/* Formatting ofpacts. */
> +
> +static void
> +print_note(const struct ofpact_note *note, struct ds *string)
> +{
> +    size_t i;
> +
> +    ds_put_cstr(string, "note:");
> +    for (i = 0; i < note->length; i++) {
> +        if (i) {
> +            ds_put_char(string, '.');
> +        }
> +        ds_put_format(string, "%02"PRIx8, note->data[i]);
> +    }
> +}
> +
> +static void
> +print_fin_timeout(const struct ofpact_fin_timeout *fin_timeout,
> +                  struct ds *s)
> +{
> +    ds_put_cstr(s, "fin_timeout(");
> +    if (fin_timeout->fin_idle_timeout) {
> +        ds_put_format(s, "idle_timeout=%"PRIu16",",
> +                      fin_timeout->fin_idle_timeout);
> +    }
> +    if (fin_timeout->fin_hard_timeout) {
> +        ds_put_format(s, "hard_timeout=%"PRIu16",",
> +                      fin_timeout->fin_hard_timeout);
> +    }
> +    ds_chomp(s, ',');
> +    ds_put_char(s, ')');
> +}
> +
> +static void
> +ofpact_format(const struct ofpact *a, struct ds *s)
> +{
> +    const struct ofpact_enqueue *enqueue;
> +    const struct ofpact_resubmit *resubmit;
> +    const struct ofpact_autopath *autopath;
> +    const struct ofpact_controller *controller;
> +    const struct ofpact_tunnel *tunnel;
> +    uint16_t port;
> +
> +    switch (a->type) {
> +    case OFPACT_END:
> +        NOT_REACHED();
> +
> +    case OFPACT_OUTPUT:
> +        port = ofpact_get_OUTPUT(a)->port;
> +        if (port < OFPP_MAX) {
> +            ds_put_format(s, "output:%"PRIu16, port);
> +        } else {
> +            ofputil_format_port(port, s);
> +            if (port == OFPP_CONTROLLER) {
> +                ds_put_format(s, ":%"PRIu16, ofpact_get_OUTPUT(a)->max_len);
> +            }
> +        }
> +        break;
> +
> +    case OFPACT_CONTROLLER:
> +        controller = ofpact_get_CONTROLLER(a);
> +        if (controller->reason == OFPR_ACTION &&
> +            controller->controller_id == 0) {
> +            ds_put_format(s, "CONTROLLER:%"PRIu16,
> +                          ofpact_get_CONTROLLER(a)->max_len);
> +        } else {
> +            enum ofp_packet_in_reason reason = controller->reason;
> +
> +            ds_put_cstr(s, "controller(");
> +            if (reason != OFPR_ACTION) {
> +                ds_put_format(s, "reason=%s,",
> +                              ofputil_packet_in_reason_to_string(reason));
> +            }
> +            if (controller->max_len != UINT16_MAX) {
> +                ds_put_format(s, "max_len=%"PRIu16",", controller->max_len);
> +            }
> +            if (controller->controller_id != 0) {
> +                ds_put_format(s, "id=%"PRIu16",", controller->controller_id);
> +            }
> +            ds_chomp(s, ',');
> +            ds_put_char(s, ')');
> +        }
> +        break;
> +
> +    case OFPACT_ENQUEUE:
> +        enqueue = ofpact_get_ENQUEUE(a);
> +        ds_put_format(s, "enqueue:");
> +        ofputil_format_port(enqueue->port, s);
> +        ds_put_format(s, "q%"PRIu32, enqueue->queue);
> +        break;
> +
> +    case OFPACT_OUTPUT_REG:
> +        ds_put_cstr(s, "output:");
> +        mf_format_subfield(&ofpact_get_OUTPUT_REG(a)->src, s);
> +        break;
> +
> +    case OFPACT_BUNDLE:
> +        bundle_format(ofpact_get_BUNDLE(a), s);
> +        break;
> +
> +    case OFPACT_SET_VLAN_VID:
> +        ds_put_format(s, "mod_vlan_vid:%"PRIu16,
> +                      ofpact_get_SET_VLAN_VID(a)->vlan_vid);
> +        break;
> +
> +    case OFPACT_SET_VLAN_PCP:
> +        ds_put_format(s, "mod_vlan_pcp:%"PRIu8,
> +                      ofpact_get_SET_VLAN_PCP(a)->vlan_pcp);
> +        break;
> +
> +    case OFPACT_STRIP_VLAN:
> +        ds_put_cstr(s, "strip_vlan");
> +        break;
> +
> +    case OFPACT_SET_ETH_SRC:
> +        ds_put_format(s, "mod_dl_src:"ETH_ADDR_FMT,
> +                      ETH_ADDR_ARGS(ofpact_get_SET_ETH_SRC(a)->mac));
> +        break;
> +
> +    case OFPACT_SET_ETH_DST:
> +        ds_put_format(s, "mod_dl_dst:"ETH_ADDR_FMT,
> +                      ETH_ADDR_ARGS(ofpact_get_SET_ETH_DST(a)->mac));
> +        break;
> +
> +    case OFPACT_SET_IPV4_SRC:
> +        ds_put_format(s, "mod_nw_src:"IP_FMT,
> +                      IP_ARGS(&ofpact_get_SET_IPV4_SRC(a)->ipv4));
> +        break;
> +
> +    case OFPACT_SET_IPV4_DST:
> +        ds_put_format(s, "mod_nw_dst:"IP_FMT,
> +                      IP_ARGS(&ofpact_get_SET_IPV4_DST(a)->ipv4));
> +        break;
> +
> +    case OFPACT_SET_IPV4_DSCP:
> +        ds_put_format(s, "mod_nw_tos:%d", ofpact_get_SET_IPV4_DSCP(a)->dscp);
> +        break;
> +
> +    case OFPACT_SET_L4_SRC_PORT:
> +        ds_put_format(s, "mod_tp_src:%d", ofpact_get_SET_L4_SRC_PORT(a)->port);
> +        break;
> +
> +    case OFPACT_SET_L4_DST_PORT:
> +        ds_put_format(s, "mod_tp_dst:%d", ofpact_get_SET_L4_DST_PORT(a)->port);
> +        break;
> +
> +    case OFPACT_REG_MOVE:
> +        nxm_format_reg_move(ofpact_get_REG_MOVE(a), s);
> +        break;
> +
> +    case OFPACT_REG_LOAD:
> +        nxm_format_reg_load(ofpact_get_REG_LOAD(a), s);
> +        break;
> +
> +    case OFPACT_DEC_TTL:
> +        ds_put_cstr(s, "dec_ttl");
> +        break;
> +
> +    case OFPACT_SET_TUNNEL:
> +        tunnel = ofpact_get_SET_TUNNEL(a);
> +        ds_put_format(s, "set_tunnel%s:%#"PRIx64,
> +                      (tunnel->tun_id > UINT32_MAX
> +                       || a->compat == OFPUTIL_NXAST_SET_TUNNEL64 ? "64" : ""),
> +                      tunnel->tun_id);
> +        break;
> +
> +    case OFPACT_SET_QUEUE:
> +        ds_put_format(s, "set_queue:%"PRIu32,
> +                      ofpact_get_SET_QUEUE(a)->queue_id);
> +        break;
> +
> +    case OFPACT_POP_QUEUE:
> +        ds_put_cstr(s, "pop_queue");
> +        break;
> +
> +    case OFPACT_FIN_TIMEOUT:
> +        print_fin_timeout(ofpact_get_FIN_TIMEOUT(a), s);
> +        break;
> +
> +    case OFPACT_RESUBMIT:
> +        resubmit = ofpact_get_RESUBMIT(a);
> +        if (resubmit->in_port != OFPP_IN_PORT && resubmit->table_id == 255) {
> +            ds_put_format(s, "resubmit:%"PRIu16, resubmit->in_port);
> +        } else {
> +            ds_put_format(s, "resubmit(");
> +            if (resubmit->in_port != OFPP_IN_PORT) {
> +                ofputil_format_port(resubmit->in_port, s);
> +            }
> +            ds_put_char(s, ',');
> +            if (resubmit->table_id != 255) {
> +                ds_put_format(s, "%"PRIu8, resubmit->table_id);
> +            }
> +            ds_put_char(s, ')');
> +        }
> +        break;
> +
> +    case OFPACT_LEARN:
> +        learn_format(ofpact_get_LEARN(a), s);
> +        break;
> +
> +    case OFPACT_MULTIPATH:
> +        multipath_format(ofpact_get_MULTIPATH(a), s);
> +        break;
> +
> +    case OFPACT_AUTOPATH:
> +        autopath = ofpact_get_AUTOPATH(a);
> +        ds_put_format(s, "autopath(%u,", autopath->port);
> +        mf_format_subfield(&autopath->dst, s);
> +        ds_put_char(s, ')');
> +        break;
> +
> +    case OFPACT_NOTE:
> +        print_note(ofpact_get_NOTE(a), s);
> +        break;
> +
> +    case OFPACT_EXIT:
> +        ds_put_cstr(s, "exit");
> +        break;
> +    }
> +}
> +
> +/* Appends a string representing the actions in 'ofpacts' (terminated by
> + * OFPACT_END) to 'string'. */
> +void
> +ofpacts_format(const struct ofpact *ofpacts, struct ds *string)
> +{
> +    ds_put_cstr(string, "actions=");
> +    if (ofpacts->type == OFPACT_END) {
> +        ds_put_cstr(string, "drop");
> +    } else {
> +        const struct ofpact *a;
> +
> +        OFPACT_FOR_EACH (a, ofpacts) {
> +            if (a != ofpacts) {
> +                ds_put_cstr(string, ",");
> +            }
> +            ofpact_format(a, string);
> +        }
> +    }
> +}
> +
> +/* Internal use by helpers. */
> +
> +void *
> +ofpact_put(struct ofpbuf *ofpacts, enum ofpact_type type, size_t len)
> +{
> +    struct ofpact *ofpact;
> +    unsigned int rem;
> +
> +    rem = ofpacts->size % OFPACT_ALIGNTO;
> +    if (rem) {
> +        ofpbuf_put_zeros(ofpacts, OFPACT_ALIGNTO - rem);
> +    }
> +
> +    ofpact = ofpacts->l2 = ofpbuf_put_uninit(ofpacts, len);
> +    ofpact_init(ofpact, type, len);
> +    return ofpact;
> +}
> +
> +void
> +ofpact_init(struct ofpact *ofpact, enum ofpact_type type, size_t len)
> +{
> +    memset(ofpact, 0, len);
> +    ofpact->type = type;
> +    ofpact->compat = OFPUTIL_ACTION_INVALID;
> +    ofpact->len = len;
> +}
> +
> +/* Updates 'ofpact->len' to the number of bytes in the tail of 'ofpacts'
> + * starting at 'ofpact'.
> + *
> + * This is the correct way to update a variable-length ofpact's length after
> + * adding the variable-length part of the payload.  (See the large comment
> + * near the end of ofp-actions.h for more information.) */
> +void
> +ofpact_update_len(struct ofpbuf *ofpacts, struct ofpact *ofpact)
> +{
> +    assert(ofpact == ofpacts->l2);
> +    ofpact->len = (char *) ofpbuf_tail(ofpacts) - (char *) ofpact;
> +}
> diff --git a/lib/ofp-actions.h b/lib/ofp-actions.h
> new file mode 100644
> index 0000000..7db01a5
> --- /dev/null
> +++ b/lib/ofp-actions.h
> @@ -0,0 +1,479 @@
> +/*
> + * Copyright (c) 2012 Nicira Networks.
> + *
> + * Licensed under the Apache License, Version 2.0 (the "License");
> + * you may not use this file except in compliance with the License.
> + * You may obtain a copy of the License at:
> + *
> + *     http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing, software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> + * See the License for the specific language governing permissions and
> + * limitations under the License.
> + */
> +
> +#ifndef OFP_ACTIONS_H
> +#define OFP_ACTIONS_H 1
> +
> +#include <stdint.h>
> +#include "meta-flow.h"
> +#include "ofp-errors.h"
> +#include "ofp-util.h"
> +#include "openflow/openflow.h"
> +#include "openflow/nicira-ext.h"
> +#include "openvswitch/types.h"
> +
> +/* List of OVS abstracted actions.
> + *
> + * This macro is used directly only internally by this header, but the list is
> + * still of interest to developers.
> + *
> + * Each DEFINE_OFPACT invocation has the following parameters:
> + *
> + * 1. <ENUM>, used below in the enum definition of OFPACT_<ENUM>, and
> + *    elsewhere.
> + *
> + * 2. <STRUCT> corresponding to a structure "struct <STRUCT>", that must be
> + *    defined below.  This structure must be an abstract definition of the
> + *    action.  Its first member must have type "struct ofpact" and name
> + *    "ofpact".  It may be fixed length or end with a flexible array member
> + *    (e.g. "int member[];").
> + *
> + * 3. <MEMBER>, which has one of two possible values:
> + *
> + *        - If "struct <STRUCT>" is fixed-length, it must be "ofpact".
> + *
> + *        - If "struct <STRUCT>" is variable-length, it must be the name of the
> + *          flexible array member.
> + */
> +#define OFPACTS                                                     \
> +    /* Sentinel. */                                                 \
> +    DEFINE_OFPACT(END,             ofpact_null,          ofpact)    \
> +                                                                    \
> +    /* Output. */                                                   \
> +    DEFINE_OFPACT(OUTPUT,          ofpact_output,        ofpact)    \
> +    DEFINE_OFPACT(CONTROLLER,      ofpact_controller,    ofpact)    \
> +    DEFINE_OFPACT(ENQUEUE,         ofpact_enqueue,       ofpact)    \
> +    DEFINE_OFPACT(OUTPUT_REG,      ofpact_output_reg,    ofpact)    \
> +    DEFINE_OFPACT(BUNDLE,          ofpact_bundle,        slaves)    \
> +                                                                    \
> +    /* Header changes. */                                           \
> +    DEFINE_OFPACT(SET_VLAN_VID,    ofpact_vlan_vid,      ofpact)    \
> +    DEFINE_OFPACT(SET_VLAN_PCP,    ofpact_vlan_pcp,      ofpact)    \
> +    DEFINE_OFPACT(STRIP_VLAN,      ofpact_null,          ofpact)    \
> +    DEFINE_OFPACT(SET_ETH_SRC,     ofpact_mac,           ofpact)    \
> +    DEFINE_OFPACT(SET_ETH_DST,     ofpact_mac,           ofpact)    \
> +    DEFINE_OFPACT(SET_IPV4_SRC,    ofpact_ipv4,          ofpact)    \
> +    DEFINE_OFPACT(SET_IPV4_DST,    ofpact_ipv4,          ofpact)    \
> +    DEFINE_OFPACT(SET_IPV4_DSCP,   ofpact_dscp,          ofpact)    \
> +    DEFINE_OFPACT(SET_L4_SRC_PORT, ofpact_l4_port,       ofpact)    \
> +    DEFINE_OFPACT(SET_L4_DST_PORT, ofpact_l4_port,       ofpact)    \
> +    DEFINE_OFPACT(REG_MOVE,        ofpact_reg_move,      ofpact)    \
> +    DEFINE_OFPACT(REG_LOAD,        ofpact_reg_load,      ofpact)    \
> +    DEFINE_OFPACT(DEC_TTL,         ofpact_null,          ofpact)    \
> +                                                                    \
> +    /* Metadata. */                                                 \
> +    DEFINE_OFPACT(SET_TUNNEL,      ofpact_tunnel,        ofpact)    \
> +    DEFINE_OFPACT(SET_QUEUE,       ofpact_queue,         ofpact)    \
> +    DEFINE_OFPACT(POP_QUEUE,       ofpact_null,          ofpact)    \
> +    DEFINE_OFPACT(FIN_TIMEOUT,     ofpact_fin_timeout,   ofpact)    \
> +                                                                    \
> +    /* Flow table interaction. */                                   \
> +    DEFINE_OFPACT(RESUBMIT,        ofpact_resubmit,      ofpact)    \
> +    DEFINE_OFPACT(LEARN,           ofpact_learn,         specs)     \
> +                                                                    \
> +    /* Arithmetic. */                                               \
> +    DEFINE_OFPACT(MULTIPATH,       ofpact_multipath,     ofpact)    \
> +    DEFINE_OFPACT(AUTOPATH,        ofpact_autopath,      ofpact)    \
> +                                                                    \
> +    /* Other. */                                                    \
> +    DEFINE_OFPACT(NOTE,            ofpact_note,          data)      \
> +    DEFINE_OFPACT(EXIT,            ofpact_null,          ofpact)
> +
> +/* enum ofpact_type, with a member OFPACT_<ENUM> for each action. */
> +enum OVS_PACKED_ENUM ofpact_type {
> +#define DEFINE_OFPACT(ENUM, STRUCT, MEMBER) OFPACT_##ENUM,
> +    OFPACTS
> +#undef DEFINE_OFPACT
> +};
> +
> +/* N_OFPACTS, the number of values of "enum ofpact_type". */
> +enum {
> +    N_OFPACTS =
> +#define DEFINE_OFPACT(ENUM, STRUCT, MEMBER) + 1
> +    OFPACTS
> +#undef DEFINE_OFPACT
> +};
> +
> +/* Header for an action.
> + *
> + * Each action is a structure "struct ofpact_*" that begins with "struct
> + * ofpact", usually followed by other data that describes the action.  Actions
> + * are padded out to a multiple of OFPACT_ALIGNTO bytes in length. */
> +struct ofpact {
> +    enum ofpact_type type;      /* OFPACT_*. */
> +    enum ofputil_action_code compat; /* Original type when added, if any. */
> +    uint16_t len;               /* Length of the action, in bytes, including
> +                                 * struct ofpact, excluding padding. */
> +};
> +
> +#ifdef __GNUC__
> +/* Make sure that OVS_PACKED_ENUM really worked. */
> +BUILD_ASSERT_DECL(sizeof(struct ofpact) == 4);
> +#endif
> +
> +/* Alignment. */
> +#define OFPACT_ALIGNTO 8
> +#define OFPACT_ALIGN(SIZE) ROUND_UP(SIZE, OFPACT_ALIGNTO)
> +
> +static inline struct ofpact *
> +ofpact_next(const struct ofpact *ofpact)
> +{
> +    return (void *) ((uint8_t *) ofpact + OFPACT_ALIGN(ofpact->len));
> +}
> +
> +/* Assigns POS to each action starting at OFPACTS in turn, assuming that the
> + * set of actions is terminated by OFPACT_END. */
> +#define OFPACT_FOR_EACH(POS, OFPACTS)                           \
> +    for ((POS) = (OFPACTS); (POS)->type != OFPACT_END;          \
> +         (POS) = ofpact_next(POS))
> +
> +/* Action structure for each OFPACT_*. */
> +
> +/* OFPACT_END, OFPACT_STRIP_VLAN, OFPACT_DEC_TTL, OFPACT_POP_QUEUE,
> + * OFPACT_EXIT.
> + *
> + * Used for OFPAT10_STRIP_VLAN, NXAST_DEC_TTL, NXAST_POP_QUEUE, NXAST_EXIT.
> + *
> + * Action structure for actions that do not have any extra data beyond the
> + * action type. */
> +struct ofpact_null {
> +    struct ofpact ofpact;
> +};
> +
> +/* OFPACT_OUTPUT.
> + *
> + * Used for OFPAT10_OUTPUT. */
> +struct ofpact_output {
> +    struct ofpact ofpact;
> +    uint16_t port;              /* Output port. */
> +    uint16_t max_len;           /* Max send len, for port OFPP_CONTROLLER. */
> +};
> +
> +/* OFPACT_CONTROLLER.
> + *
> + * Used for NXAST_CONTROLLER. */
> +struct ofpact_controller {
> +    struct ofpact ofpact;
> +    uint16_t max_len;           /* Maximum length to send to controller. */
> +    uint16_t controller_id;     /* Controller ID to send packet-in. */
> +    enum ofp_packet_in_reason reason; /* Reason to put in packet-in. */
> +};
> +
> +/* OFPACT_ENQUEUE.
> + *
> + * Used for OFPAT10_ENQUEUE. */
> +struct ofpact_enqueue {
> +    struct ofpact ofpact;
> +    uint16_t port;
> +    uint32_t queue;
> +};
> +
> +/* OFPACT_OUTPUT_REG.
> + *
> + * Used for NXAST_OUTPUT_REG. */
> +struct ofpact_output_reg {
> +    struct ofpact ofpact;
> +    struct mf_subfield src;
> +    uint16_t max_len;
> +};
> +
> +/* OFPACT_BUNDLE.
> + *
> + * Used for NXAST_BUNDLE. */
> +struct ofpact_bundle {
> +    struct ofpact ofpact;
> +
> +    /* Slave choice algorithm to apply to hash value. */
> +    enum nx_bd_algorithm algorithm;
> +
> +    /* What fields to hash and how. */
> +    enum nx_hash_fields fields;
> +    uint16_t basis;             /* Universal hash parameter. */
> +
> +    struct mf_subfield dst;
> +
> +    /* Slaves for output. */
> +    unsigned int n_slaves;
> +    uint16_t slaves[];
> +};
> +
> +/* OFPACT_SET_VLAN_VID.
> + *
> + * Used for OFPAT10_SET_VLAN_VID. */
> +struct ofpact_vlan_vid {
> +    struct ofpact ofpact;
> +    uint16_t vlan_vid;          /* VLAN VID in low 12 bits, 0 in other bits. */
> +};
> +
> +/* OFPACT_SET_VLAN_PCP.
> + *
> + * Used for OFPAT10_SET_VLAN_PCP. */
> +struct ofpact_vlan_pcp {
> +    struct ofpact ofpact;
> +    uint8_t vlan_pcp;           /* VLAN PCP in low 3 bits, 0 in other bits. */
> +};
> +
> +/* OFPACT_SET_ETH_SRC, OFPACT_SET_ETH_DST.
> + *
> + * Used for OFPAT10_SET_DL_SRC, OFPAT10_SET_DL_DST. */
> +struct ofpact_mac {
> +    struct ofpact ofpact;
> +    uint8_t mac[ETH_ADDR_LEN];
> +};
> +
> +/* OFPACT_SET_IPV4_SRC, OFPACT_SET_IPV4_DST.
> + *
> + * Used for OFPAT10_SET_NW_SRC, OFPAT10_SET_NW_DST. */
> +struct ofpact_ipv4 {
> +    struct ofpact ofpact;
> +    ovs_be32 ipv4;
> +};
> +
> +/* OFPACT_SET_IPV4_DSCP.
> + *
> + * Used for OFPAT10_SET_NW_TOS. */
> +struct ofpact_dscp {
> +    struct ofpact ofpact;
> +    uint8_t dscp;               /* DSCP in high 6 bits, rest ignored. */
> +};
> +
> +/* OFPACT_SET_L4_SRC_PORT, OFPACT_SET_L4_DST_PORT.
> + *
> + * Used for OFPAT10_SET_TP_SRC, OFPAT10_SET_TP_DST. */
> +struct ofpact_l4_port {
> +    struct ofpact ofpact;
> +    uint16_t port;              /* TCP or UDP port number. */
> +};
> +
> +/* OFPACT_REG_MOVE.
> + *
> + * Used for NXAST_REG_MOVE. */
> +struct ofpact_reg_move {
> +    struct ofpact ofpact;
> +    struct mf_subfield src;
> +    struct mf_subfield dst;
> +};
> +
> +/* OFPACT_REG_LOAD.
> + *
> + * Used for NXAST_REG_LOAD. */
> +struct ofpact_reg_load {
> +    struct ofpact ofpact;
> +    struct mf_subfield dst;
> +    uint64_t value;
> +};
> +
> +/* OFPACT_SET_TUNNEL.
> + *
> + * Used for NXAST_SET_TUNNEL, NXAST_SET_TUNNEL64. */
> +struct ofpact_tunnel {
> +    struct ofpact ofpact;
> +    uint64_t tun_id;
> +};
> +
> +/* OFPACT_SET_QUEUE.
> + *
> + * Used for NXAST_SET_QUEUE. */
> +struct ofpact_queue {
> +    struct ofpact ofpact;
> +    uint32_t queue_id;
> +};
> +
> +/* OFPACT_FIN_TIMEOUT.
> + *
> + * Used for NXAST_FIN_TIMEOUT. */
> +struct ofpact_fin_timeout {
> +    struct ofpact ofpact;
> +    uint16_t fin_idle_timeout;
> +    uint16_t fin_hard_timeout;
> +};
> +
> +/* OFPACT_RESUBMIT.
> + *
> + * Used for NXAST_RESUBMIT, NXAST_RESUBMIT_TABLE. */
> +struct ofpact_resubmit {
> +    struct ofpact ofpact;
> +    uint16_t in_port;
> +    uint8_t table_id;
> +};
> +
> +/* Part of struct ofpact_learn, below. */
> +struct ofpact_learn_spec {
> +    int n_bits;
> +
> +    int src_type;
> +    struct mf_subfield src;
> +    union mf_subvalue src_imm;
> +
> +    int dst_type;
> +    struct mf_subfield dst;
> +};
> +
> +/* OFPACT_LEARN.
> + *
> + * Used for NXAST_LEARN. */
> +struct ofpact_learn {
> +    struct ofpact ofpact;
> +
> +    uint16_t idle_timeout;      /* Idle time before discarding (seconds). */
> +    uint16_t hard_timeout;      /* Max time before discarding (seconds). */
> +    uint16_t priority;          /* Priority level of flow entry. */
> +    uint64_t cookie;            /* Cookie for new flow. */
> +    uint16_t flags;             /* Either 0 or OFPFF_SEND_FLOW_REM. */
> +    uint8_t table_id;           /* Table to insert flow entry. */
> +    uint16_t fin_idle_timeout;  /* Idle timeout after FIN, if nonzero. */
> +    uint16_t fin_hard_timeout;  /* Hard timeout after FIN, if nonzero. */
> +
> +    unsigned int n_specs;
> +    struct ofpact_learn_spec specs[];
> +};
> +
> +/* OFPACT_MULTIPATH.
> + *
> + * Used for NXAST_MULTIPATH. */
> +struct ofpact_multipath {
> +    struct ofpact ofpact;
> +
> +    /* What fields to hash and how. */
> +    enum nx_hash_fields fields;
> +    uint16_t basis;             /* Universal hash parameter. */
> +
> +    /* Multipath link choice algorithm to apply to hash value. */
> +    enum nx_mp_algorithm algorithm;
> +    uint16_t max_link;          /* Number of output links, minus 1. */
> +    uint32_t arg;               /* Algorithm-specific argument. */
> +
> +    /* Where to store the result. */
> +    struct mf_subfield dst;
> +};
> +
> +/* OFPACT_AUTOPATH.
> + *
> + * Used for NXAST_AUTOPATH. */
> +struct ofpact_autopath {
> +    struct ofpact ofpact;
> +    struct mf_subfield dst;
> +    uint32_t port;
> +};
> +
> +/* OFPACT_NOTE.
> + *
> + * Used for NXAST_NOTE. */
> +struct ofpact_note {
> +    struct ofpact ofpact;
> +    size_t length;
> +    uint8_t data[];
> +};
> +
> +/* Converting OpenFlow to ofpacts. */
> +enum ofperr ofpacts_pull_openflow(struct ofpbuf *openflow,
> +                                  unsigned int actions_len,
> +                                  struct ofpbuf *ofpacts);
> +enum ofperr ofpacts_check(const struct ofpact[],
> +                          const struct flow *, int max_ports);
> +
> +/* Converting ofpacts to OpenFlow. */
> +void ofpacts_to_openflow(const struct ofpact[], struct ofpbuf *openflow);
> +
> +/* Working with ofpacts. */
> +bool ofpacts_output_to_port(const struct ofpact[], uint16_t port);
> +bool ofpacts_equal(const struct ofpact a[], size_t a_len,
> +                   const struct ofpact b[], size_t b_len);
> +
> +/* Formatting ofpacts.
> + *
> + * (For parsing ofpacts, see ofp-parse.h.) */
> +void ofpacts_format(const struct ofpact[], struct ds *);
> +
> +/* Internal use by the helpers below. */
> +void ofpact_init(struct ofpact *, enum ofpact_type, size_t len);
> +void *ofpact_put(struct ofpbuf *, enum ofpact_type, size_t len);
> +
> +/* For each OFPACT_<ENUM> with a corresponding struct <STRUCT>, this defines
> + * the following commonly useful functions:
> + *
> + *   struct <STRUCT> *ofpact_put_<ENUM>(struct ofpbuf *ofpacts);
> + *
> + *     Appends a new 'ofpact', of length OFPACT_<ENUM>_RAW_SIZE, to 'ofpacts',
> + *     initializes it with ofpact_init_<ENUM>(), and returns it.  Also sets
> + *     'ofpacts->l2' to the returned action.
> + *
> + *     After using this function to add a variable-length action, add the
> + *     elements of the flexible array (e.g. with ofpbuf_put()), then use
> + *     ofpact_update_len() to update the length embedded into the action.
> + *     (Keep in mind the need to refresh the structure from 'ofpacts->l2' after
> + *     adding data to 'ofpacts'.)
> + *
> + *   struct <STRUCT> *ofpact_get_<ENUM>(const struct ofpact *ofpact);
> + *
> + *     Returns 'ofpact' cast to "struct <STRUCT> *".  'ofpact->type' must be
> + *     OFPACT_<ENUM>.
> + *
> + * as well as the following more rarely useful definitions:
> + *
> + *   void ofpact_init_<ENUM>(struct <STRUCT> *ofpact);
> + *
> + *     Initializes the parts of 'ofpact' that identify it as having type
> + *     OFPACT_<ENUM> and length OFPACT_<ENUM>_RAW_SIZE and zeros the rest.
> + *
> + *   <ENUM>_RAW_SIZE
> + *
> + *     The size of the action structure.  For a fixed-length action, this is
> + *     sizeof(struct <STRUCT>).  For a variable-length action, this is the
> + *     offset to the variable-length part.
> + *
> + *   <ENUM>_SIZE
> + *
> + *     An integer constant, the value of OFPACT_<ENUM>_RAW_SIZE rounded up to a
> + *     multiple of OFPACT_ALIGNTO.
> + */
> +#define DEFINE_OFPACT(ENUM, STRUCT, MEMBER)                             \
> +    BUILD_ASSERT_DECL(offsetof(struct STRUCT, ofpact) == 0);            \
> +                                                                        \
> +    enum { OFPACT_##ENUM##_RAW_SIZE                                     \
> +           = (offsetof(struct STRUCT, MEMBER)                           \
> +              ? offsetof(struct STRUCT, MEMBER)                         \
> +              : sizeof(struct STRUCT)) };                               \
> +                                                                        \
> +    enum { OFPACT_##ENUM##_SIZE                                         \
> +           = ROUND_UP(OFPACT_##ENUM##_RAW_SIZE, OFPACT_ALIGNTO) };      \
> +                                                                        \
> +    static inline struct STRUCT *                                       \
> +    ofpact_get_##ENUM(const struct ofpact *ofpact)                      \
> +    {                                                                   \
> +        assert(ofpact->type == OFPACT_##ENUM);                          \
> +        return (struct STRUCT *) ofpact;                                \
> +    }                                                                   \
> +                                                                        \
> +    static inline struct STRUCT *                                       \
> +    ofpact_put_##ENUM(struct ofpbuf *ofpacts)                           \
> +    {                                                                   \
> +        return ofpact_put(ofpacts, OFPACT_##ENUM,                       \
> +                          OFPACT_##ENUM##_RAW_SIZE);                    \
> +    }                                                                   \
> +                                                                        \
> +    static inline void                                                  \
> +    ofpact_init_##ENUM(struct STRUCT *ofpact)                           \
> +    {                                                                   \
> +        ofpact_init(&ofpact->ofpact, OFPACT_##ENUM,                     \
> +                    OFPACT_##ENUM##_RAW_SIZE);                          \
> +    }
> +OFPACTS
> +#undef DEFINE_OFPACT
> +
> +void ofpact_update_len(struct ofpbuf *, struct ofpact *);
> +
> +#endif /* ofp-actions.h */
> diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c
> index 1d331bb..ae70fb5 100644
> --- a/lib/ofp-parse.c
> +++ b/lib/ofp-parse.c
> @@ -28,9 +28,10 @@
>  #include "dynamic-string.h"
>  #include "learn.h"
>  #include "meta-flow.h"
> -#include "netdev.h"
>  #include "multipath.h"
> +#include "netdev.h"
>  #include "nx-match.h"
> +#include "ofp-actions.h"
>  #include "ofp-util.h"
>  #include "ofpbuf.h"
>  #include "openflow/openflow.h"
> @@ -122,107 +123,73 @@ str_to_ip(const char *str, ovs_be32 *ip)
>      *ip = in_addr.s_addr;
>  }
>
> -static struct ofp_action_output *
> -put_output_action(struct ofpbuf *b, uint16_t port)
> -{
> -    struct ofp_action_output *oao;
> -
> -    oao = ofputil_put_OFPAT10_OUTPUT(b);
> -    oao->port = htons(port);
> -    return oao;
> -}
> -
>  static void
> -parse_enqueue(struct ofpbuf *b, char *arg)
> +parse_enqueue(char *arg, struct ofpbuf *ofpacts)
>  {
>      char *sp = NULL;
>      char *port = strtok_r(arg, ":q", &sp);
>      char *queue = strtok_r(NULL, "", &sp);
> -    struct ofp_action_enqueue *oae;
> +    struct ofpact_enqueue *enqueue;
>
>      if (port == NULL || queue == NULL) {
>          ovs_fatal(0, "\"enqueue\" syntax is \"enqueue:PORT:QUEUE\"");
>      }
>
> -    oae = ofputil_put_OFPAT10_ENQUEUE(b);
> -    oae->port = htons(str_to_u32(port));
> -    oae->queue_id = htonl(str_to_u32(queue));
> +    enqueue = ofpact_put_ENQUEUE(ofpacts);
> +    enqueue->port = str_to_u32(port);
> +    enqueue->queue = str_to_u32(queue);
>  }
>
>  static void
> -parse_output(struct ofpbuf *b, char *arg)
> +parse_output(char *arg, struct ofpbuf *ofpacts)
>  {
>      if (strchr(arg, '[')) {
> -        struct nx_action_output_reg *naor;
> -        struct mf_subfield src;
> -
> -        mf_parse_subfield(&src, arg);
> +        struct ofpact_output_reg *output_reg;
>
> -        naor = ofputil_put_NXAST_OUTPUT_REG(b);
> -        naor->ofs_nbits = nxm_encode_ofs_nbits(src.ofs, src.n_bits);
> -        naor->src = htonl(src.field->nxm_header);
> -        naor->max_len = htons(UINT16_MAX);
> +        output_reg = ofpact_put_OUTPUT_REG(ofpacts);
> +        mf_parse_subfield(&output_reg->src, arg);
> +        output_reg->max_len = UINT16_MAX;
>      } else {
> -        put_output_action(b, str_to_u32(arg));
> +        struct ofpact_output *output;
> +
> +        output = ofpact_put_OUTPUT(ofpacts);
> +        output->port = str_to_u32(arg);
> +        output->max_len = output->port == OFPP_CONTROLLER ? UINT16_MAX : 0;
>      }
>  }
>
>  static void
> -parse_resubmit(struct ofpbuf *b, char *arg)
> +parse_resubmit(char *arg, struct ofpbuf *ofpacts)
>  {
> -    struct nx_action_resubmit *nar;
> +    struct ofpact_resubmit *resubmit;
>      char *in_port_s, *table_s;
> -    uint16_t in_port;
> -    uint8_t table;
> +
> +    resubmit = ofpact_put_RESUBMIT(ofpacts);
>
>      in_port_s = strsep(&arg, ",");
>      if (in_port_s && in_port_s[0]) {
> -        if (!ofputil_port_from_string(in_port_s, &in_port)) {
> -            in_port = str_to_u32(in_port_s);
> +        if (!ofputil_port_from_string(in_port_s, &resubmit->in_port)) {
> +            resubmit->in_port = str_to_u32(in_port_s);
>          }
>      } else {
> -        in_port = OFPP_IN_PORT;
> +        resubmit->in_port = OFPP_IN_PORT;
>      }
>
>      table_s = strsep(&arg, ",");
> -    table = table_s && table_s[0] ? str_to_u32(table_s) : 255;
> +    resubmit->table_id = table_s && table_s[0] ? str_to_u32(table_s) : 255;
>
> -    if (in_port == OFPP_IN_PORT && table == 255) {
> +    if (resubmit->in_port == OFPP_IN_PORT && resubmit->table_id == 255) {
>          ovs_fatal(0, "at least one \"in_port\" or \"table\" must be specified "
>                    " on resubmit");
>      }
> -
> -    if (in_port != OFPP_IN_PORT && table == 255) {
> -        nar = ofputil_put_NXAST_RESUBMIT(b);
> -    } else {
> -        nar = ofputil_put_NXAST_RESUBMIT_TABLE(b);
> -        nar->table = table;
> -    }
> -    nar->in_port = htons(in_port);
> -}
> -
> -static void
> -parse_set_tunnel(struct ofpbuf *b, const char *arg)
> -{
> -    uint64_t tun_id = str_to_u64(arg);
> -    if (tun_id > UINT32_MAX) {
> -        ofputil_put_NXAST_SET_TUNNEL64(b)->tun_id = htonll(tun_id);
> -    } else {
> -        ofputil_put_NXAST_SET_TUNNEL(b)->tun_id = htonl(tun_id);
> -    }
>  }
>
>  static void
> -parse_note(struct ofpbuf *b, const char *arg)
> +parse_note(const char *arg, struct ofpbuf *ofpacts)
>  {
> -    size_t start_ofs = b->size;
> -    struct nx_action_note *nan;
> -    int remainder;
> -    size_t len;
> +    struct ofpact_note *note;
>
> -    nan = ofputil_put_NXAST_NOTE(b);
> -
> -    b->size -= sizeof nan->note;
> +    note = ofpact_put_NOTE(ofpacts);
>      while (*arg != '\0') {
>          uint8_t byte;
>          bool ok;
> @@ -238,32 +205,27 @@ parse_note(struct ofpbuf *b, const char *arg)
>          if (!ok) {
>              ovs_fatal(0, "bad hex digit in `note' argument");
>          }
> -        ofpbuf_put(b, &byte, 1);
> +        ofpbuf_put(ofpacts, &byte, 1);
>
> -        arg += 2;
> -    }
> +        note = ofpacts->l2;
> +        note->length++;
>
> -    len = b->size - start_ofs;
> -    remainder = len % OFP_ACTION_ALIGN;
> -    if (remainder) {
> -        ofpbuf_put_zeros(b, OFP_ACTION_ALIGN - remainder);
> +        arg += 2;
>      }
> -    nan = (struct nx_action_note *)((char *)b->data + start_ofs);
> -    nan->len = htons(b->size - start_ofs);
> +    ofpact_update_len(ofpacts, &note->ofpact);
>  }
>
>  static void
>  parse_fin_timeout(struct ofpbuf *b, char *arg)
>  {
> -    struct nx_action_fin_timeout *naft;
> +    struct ofpact_fin_timeout *oft = ofpact_put_FIN_TIMEOUT(b);
>      char *key, *value;
>
> -    naft = ofputil_put_NXAST_FIN_TIMEOUT(b);
>      while (ofputil_parse_key_value(&arg, &key, &value)) {
>          if (!strcmp(key, "idle_timeout")) {
> -            naft->fin_idle_timeout = htons(str_to_u16(value, key));
> +            oft->fin_idle_timeout = str_to_u16(value, key);
>          } else if (!strcmp(key, "hard_timeout")) {
> -            naft->fin_hard_timeout = htons(str_to_u16(value, key));
> +            oft->fin_hard_timeout = str_to_u16(value, key);
>          } else {
>              ovs_fatal(0, "invalid key '%s' in 'fin_timeout' argument", key);
>          }
> @@ -301,121 +263,142 @@ parse_controller(struct ofpbuf *b, char *arg)
>      }
>
>      if (reason == OFPR_ACTION && controller_id == 0) {
> -        put_output_action(b, OFPP_CONTROLLER)->max_len = htons(max_len);
> +        struct ofpact_output *output;
> +
> +        output = ofpact_put_OUTPUT(b);
> +        output->port = OFPP_CONTROLLER;
> +        output->max_len = max_len;
>      } else {
> -        struct nx_action_controller *nac;
> +        struct ofpact_controller *controller;
>
> -        nac = ofputil_put_NXAST_CONTROLLER(b);
> -        nac->max_len = htons(max_len);
> -        nac->reason = reason;
> -        nac->controller_id = htons(controller_id);
> +        controller = ofpact_put_CONTROLLER(b);
> +        controller->max_len = max_len;
> +        controller->reason = reason;
> +        controller->controller_id = controller_id;
>      }
>  }
>
>  static void
>  parse_named_action(enum ofputil_action_code code, const struct flow *flow,
> -                   struct ofpbuf *b, char *arg)
> +                   char *arg, struct ofpbuf *ofpacts)
>  {
> -    struct ofp_action_dl_addr *oada;
> -    struct ofp_action_vlan_pcp *oavp;
> -    struct ofp_action_vlan_vid *oavv;
> -    struct ofp_action_nw_addr *oana;
> -    struct ofp_action_tp_port *oata;
> +    struct ofpact_tunnel *tunnel;
> +    uint16_t vid;
> +    ovs_be32 ip;
> +    uint8_t pcp;
> +    uint8_t tos;
>
>      switch (code) {
>      case OFPUTIL_ACTION_INVALID:
>          NOT_REACHED();
>
>      case OFPUTIL_OFPAT10_OUTPUT:
> -        parse_output(b, arg);
> +        parse_output(arg, ofpacts);
>          break;
>
>      case OFPUTIL_OFPAT10_SET_VLAN_VID:
> -        oavv = ofputil_put_OFPAT10_SET_VLAN_VID(b);
> -        oavv->vlan_vid = htons(str_to_u32(arg));
> +        vid = str_to_u32(arg);
> +        if (vid & ~VLAN_VID_MASK) {
> +            ovs_fatal(0, "%s: not a valid VLAN VID", arg);
> +        }
> +        ofpact_put_SET_VLAN_VID(ofpacts)->vlan_vid = vid;
>          break;
>
>      case OFPUTIL_OFPAT10_SET_VLAN_PCP:
> -        oavp = ofputil_put_OFPAT10_SET_VLAN_PCP(b);
> -        oavp->vlan_pcp = str_to_u32(arg);
> +        pcp = str_to_u32(arg);
> +        if (pcp & ~7) {
> +            ovs_fatal(0, "%s: not a valid VLAN PCP", arg);
> +        }
> +        ofpact_put_SET_VLAN_PCP(ofpacts)->vlan_pcp = pcp;
>          break;
>
>      case OFPUTIL_OFPAT10_STRIP_VLAN:
> -        ofputil_put_OFPAT10_STRIP_VLAN(b);
> +        ofpact_put_STRIP_VLAN(ofpacts);
>          break;
>
>      case OFPUTIL_OFPAT10_SET_DL_SRC:
> +        str_to_mac(arg, ofpact_put_SET_ETH_SRC(ofpacts)->mac);
> +        break;
> +
>      case OFPUTIL_OFPAT10_SET_DL_DST:
> -        oada = ofputil_put_action(code, b);
> -        str_to_mac(arg, oada->dl_addr);
> +        str_to_mac(arg, ofpact_put_SET_ETH_DST(ofpacts)->mac);
>          break;
>
>      case OFPUTIL_OFPAT10_SET_NW_SRC:
> +        str_to_ip(arg, &ip);
> +        ofpact_put_SET_IPV4_SRC(ofpacts)->ipv4 = ip;
> +        break;
> +
>      case OFPUTIL_OFPAT10_SET_NW_DST:
> -        oana = ofputil_put_action(code, b);
> -        str_to_ip(arg, &oana->nw_addr);
> +        str_to_ip(arg, &ip);
> +        ofpact_put_SET_IPV4_DST(ofpacts)->ipv4 = ip;
>          break;
>
>      case OFPUTIL_OFPAT10_SET_NW_TOS:
> -        ofputil_put_OFPAT10_SET_NW_TOS(b)->nw_tos = str_to_u32(arg);
> +        tos = str_to_u32(arg);
> +        if (tos & ~IP_DSCP_MASK) {
> +            ovs_fatal(0, "%s: not a valid TOS", arg);
> +        }
> +        ofpact_put_SET_IPV4_DSCP(ofpacts)->dscp = tos;
>          break;
>
>      case OFPUTIL_OFPAT10_SET_TP_SRC:
> +        ofpact_put_SET_L4_SRC_PORT(ofpacts)->port = str_to_u32(arg);
> +        break;
> +
>      case OFPUTIL_OFPAT10_SET_TP_DST:
> -        oata = ofputil_put_action(code, b);
> -        oata->tp_port = htons(str_to_u32(arg));
> +        ofpact_put_SET_L4_DST_PORT(ofpacts)->port = str_to_u32(arg);
>          break;
>
>      case OFPUTIL_OFPAT10_ENQUEUE:
> -        parse_enqueue(b, arg);
> +        parse_enqueue(arg, ofpacts);
>          break;
>
>      case OFPUTIL_NXAST_RESUBMIT:
> -        parse_resubmit(b, arg);
> +        parse_resubmit(arg, ofpacts);
>          break;
>
>      case OFPUTIL_NXAST_SET_TUNNEL:
> -        parse_set_tunnel(b, arg);
> +    case OFPUTIL_NXAST_SET_TUNNEL64:
> +        tunnel = ofpact_put_SET_TUNNEL(ofpacts);
> +        tunnel->ofpact.compat = code;
> +        tunnel->tun_id = str_to_u64(arg);
>          break;
>
>      case OFPUTIL_NXAST_SET_QUEUE:
> -        ofputil_put_NXAST_SET_QUEUE(b)->queue_id = htonl(str_to_u32(arg));
> +        ofpact_put_SET_QUEUE(ofpacts)->queue_id = str_to_u32(arg);
>          break;
>
>      case OFPUTIL_NXAST_POP_QUEUE:
> -        ofputil_put_NXAST_POP_QUEUE(b);
> +        ofpact_put_POP_QUEUE(ofpacts);
>          break;
>
>      case OFPUTIL_NXAST_REG_MOVE:
> -        nxm_parse_reg_move(ofputil_put_NXAST_REG_MOVE(b), arg);
> +        nxm_parse_reg_move(ofpact_put_REG_MOVE(ofpacts), arg);
>          break;
>
>      case OFPUTIL_NXAST_REG_LOAD:
> -        nxm_parse_reg_load(ofputil_put_NXAST_REG_LOAD(b), arg);
> +        nxm_parse_reg_load(ofpact_put_REG_LOAD(ofpacts), arg);
>          break;
>
>      case OFPUTIL_NXAST_NOTE:
> -        parse_note(b, arg);
> -        break;
> -
> -    case OFPUTIL_NXAST_SET_TUNNEL64:
> -        ofputil_put_NXAST_SET_TUNNEL64(b)->tun_id = htonll(str_to_u64(arg));
> +        parse_note(arg, ofpacts);
>          break;
>
>      case OFPUTIL_NXAST_MULTIPATH:
> -        multipath_parse(ofputil_put_NXAST_MULTIPATH(b), arg);
> +        multipath_parse(ofpact_put_MULTIPATH(ofpacts), arg);
>          break;
>
>      case OFPUTIL_NXAST_AUTOPATH:
> -        autopath_parse(ofputil_put_NXAST_AUTOPATH(b), arg);
> +        autopath_parse(ofpact_put_AUTOPATH(ofpacts), arg);
>          break;
>
>      case OFPUTIL_NXAST_BUNDLE:
> -        bundle_parse(b, arg);
> +        bundle_parse(arg, ofpacts);
>          break;
>
>      case OFPUTIL_NXAST_BUNDLE_LOAD:
> -        bundle_parse_load(b, arg);
> +        bundle_parse_load(arg, ofpacts);
>          break;
>
>      case OFPUTIL_NXAST_RESUBMIT_TABLE:
> @@ -423,29 +406,29 @@ parse_named_action(enum ofputil_action_code code, const struct flow *flow,
>          NOT_REACHED();
>
>      case OFPUTIL_NXAST_LEARN:
> -        learn_parse(b, arg, flow);
> +        learn_parse(arg, flow, ofpacts);
>          break;
>
>      case OFPUTIL_NXAST_EXIT:
> -        ofputil_put_NXAST_EXIT(b);
> +        ofpact_put_EXIT(ofpacts);
>          break;
>
>      case OFPUTIL_NXAST_DEC_TTL:
> -        ofputil_put_NXAST_DEC_TTL(b);
> +        ofpact_put_DEC_TTL(ofpacts);
>          break;
>
>      case OFPUTIL_NXAST_FIN_TIMEOUT:
> -        parse_fin_timeout(b, arg);
> +        parse_fin_timeout(ofpacts, arg);
>          break;
>
>      case OFPUTIL_NXAST_CONTROLLER:
> -        parse_controller(b, arg);
> +        parse_controller(ofpacts, arg);
>          break;
>      }
>  }
>
>  static void
> -str_to_action(const struct flow *flow, char *str, struct ofpbuf *b)
> +str_to_ofpacts(const struct flow *flow, char *str, struct ofpbuf *ofpacts)
>  {
>      char *pos, *act, *arg;
>      int n_actions;
> @@ -458,10 +441,8 @@ str_to_action(const struct flow *flow, char *str, struct ofpbuf *b)
>
>          code = ofputil_action_code_from_name(act);
>          if (code >= 0) {
> -            parse_named_action(code, flow, b, arg);
> +            parse_named_action(code, flow, arg, ofpacts);
>          } else if (!strcasecmp(act, "drop")) {
> -            /* A drop action in OpenFlow occurs by just not setting
> -             * an action. */
>              if (n_actions) {
>                  ovs_fatal(0, "Drop actions must not be preceded by other "
>                            "actions");
> @@ -471,12 +452,13 @@ str_to_action(const struct flow *flow, char *str, struct ofpbuf *b)
>              }
>              break;
>          } else if (ofputil_port_from_string(act, &port)) {
> -            put_output_action(b, port);
> +            ofpact_put_OUTPUT(ofpacts)->port = port;
>          } else {
>              ovs_fatal(0, "Unknown action: %s", act);
>          }
>          n_actions++;
>      }
> +    ofpact_put_END(ofpacts);
>  }
>
>  struct protocol {
> @@ -694,15 +676,15 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
>          fm->new_cookie = htonll(0);
>      }
>      if (fields & F_ACTIONS) {
> -        struct ofpbuf actions;
> +        struct ofpbuf ofpacts;
>
> -        ofpbuf_init(&actions, sizeof(union ofp_action));
> -        str_to_action(&fm->cr.flow, act_str, &actions);
> -        fm->actions = ofpbuf_steal_data(&actions);
> -        fm->n_actions = actions.size / sizeof(union ofp_action);
> +        ofpbuf_init(&ofpacts, 32);
> +        str_to_ofpacts(&fm->cr.flow, act_str, &ofpacts);
> +        fm->ofpacts_len = ofpacts.size;
> +        fm->ofpacts = ofpbuf_steal_data(&ofpacts);
>      } else {
> -        fm->actions = NULL;
> -        fm->n_actions = 0;
> +        fm->ofpacts_len = 0;
> +        fm->ofpacts = NULL;
>      }
>
>      free(string);
> @@ -714,10 +696,10 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
>   * Prints an error on stderr and aborts the program if 's' syntax is
>   * invalid. */
>  void
> -parse_ofp_actions(const char *s_, struct ofpbuf *actions)
> +parse_ofpacts(const char *s_, struct ofpbuf *ofpacts)
>  {
>      char *s = xstrdup(s_);
> -    str_to_action(NULL, s, actions);
> +    str_to_ofpacts(NULL, s, ofpacts);
>      free(s);
>  }
>
> diff --git a/lib/ofp-parse.h b/lib/ofp-parse.h
> index 3e5e62a..e930388 100644
> --- a/lib/ofp-parse.h
> +++ b/lib/ofp-parse.h
> @@ -40,7 +40,7 @@ void parse_ofp_flow_stats_request_str(struct ofputil_flow_stats_request *,
>                                        bool aggregate, const char *string);
>
>
> -void parse_ofp_actions(const char *, struct ofpbuf *actions);
> +void parse_ofpacts(const char *, struct ofpbuf *ofpacts);
>
>  char *parse_ofp_exact_flow(struct flow *, const char *);
>
> diff --git a/lib/ofp-print.c b/lib/ofp-print.c
> index 347b69c..6983af7 100644
> --- a/lib/ofp-print.c
> +++ b/lib/ofp-print.c
> @@ -36,6 +36,7 @@
>  #include "meta-flow.h"
>  #include "netdev.h"
>  #include "nx-match.h"
> +#include "ofp-actions.h"
>  #include "ofp-errors.h"
>  #include "ofp-util.h"
>  #include "ofpbuf.h"
> @@ -154,278 +155,17 @@ ofp_print_packet_in(struct ds *string, const struct ofp_header *oh,
>  }
>
>  static void
> -print_note(struct ds *string, const struct nx_action_note *nan)
> -{
> -    size_t len;
> -    size_t i;
> -
> -    ds_put_cstr(string, "note:");
> -    len = ntohs(nan->len) - offsetof(struct nx_action_note, note);
> -    for (i = 0; i < len; i++) {
> -        if (i) {
> -            ds_put_char(string, '.');
> -        }
> -        ds_put_format(string, "%02"PRIx8, nan->note[i]);
> -    }
> -}
> -
> -static void
> -ofp_print_action(struct ds *s, const union ofp_action *a,
> -                 enum ofputil_action_code code)
> -{
> -    const struct ofp_action_enqueue *oae;
> -    const struct ofp_action_dl_addr *oada;
> -    const struct nx_action_set_tunnel64 *nast64;
> -    const struct nx_action_set_tunnel *nast;
> -    const struct nx_action_set_queue *nasq;
> -    const struct nx_action_resubmit *nar;
> -    const struct nx_action_reg_move *move;
> -    const struct nx_action_reg_load *load;
> -    const struct nx_action_multipath *nam;
> -    const struct nx_action_autopath *naa;
> -    const struct nx_action_output_reg *naor;
> -    const struct nx_action_fin_timeout *naft;
> -    const struct nx_action_controller *nac;
> -    struct mf_subfield subfield;
> -    uint16_t port;
> -
> -    switch (code) {
> -    case OFPUTIL_ACTION_INVALID:
> -        NOT_REACHED();
> -
> -    case OFPUTIL_OFPAT10_OUTPUT:
> -        port = ntohs(a->output.port);
> -        if (port < OFPP_MAX) {
> -            ds_put_format(s, "output:%"PRIu16, port);
> -        } else {
> -            ofputil_format_port(port, s);
> -            if (port == OFPP_CONTROLLER) {
> -                if (a->output.max_len != htons(0)) {
> -                    ds_put_format(s, ":%"PRIu16, ntohs(a->output.max_len));
> -                } else {
> -                    ds_put_cstr(s, ":all");
> -                }
> -            }
> -        }
> -        break;
> -
> -    case OFPUTIL_OFPAT10_ENQUEUE:
> -        oae = (const struct ofp_action_enqueue *) a;
> -        ds_put_format(s, "enqueue:");
> -        ofputil_format_port(ntohs(oae->port), s);
> -        ds_put_format(s, "q%"PRIu32, ntohl(oae->queue_id));
> -        break;
> -
> -    case OFPUTIL_OFPAT10_SET_VLAN_VID:
> -        ds_put_format(s, "mod_vlan_vid:%"PRIu16,
> -                      ntohs(a->vlan_vid.vlan_vid));
> -        break;
> -
> -    case OFPUTIL_OFPAT10_SET_VLAN_PCP:
> -        ds_put_format(s, "mod_vlan_pcp:%"PRIu8, a->vlan_pcp.vlan_pcp);
> -        break;
> -
> -    case OFPUTIL_OFPAT10_STRIP_VLAN:
> -        ds_put_cstr(s, "strip_vlan");
> -        break;
> -
> -    case OFPUTIL_OFPAT10_SET_DL_SRC:
> -        oada = (const struct ofp_action_dl_addr *) a;
> -        ds_put_format(s, "mod_dl_src:"ETH_ADDR_FMT,
> -                      ETH_ADDR_ARGS(oada->dl_addr));
> -        break;
> -
> -    case OFPUTIL_OFPAT10_SET_DL_DST:
> -        oada = (const struct ofp_action_dl_addr *) a;
> -        ds_put_format(s, "mod_dl_dst:"ETH_ADDR_FMT,
> -                      ETH_ADDR_ARGS(oada->dl_addr));
> -        break;
> -
> -    case OFPUTIL_OFPAT10_SET_NW_SRC:
> -        ds_put_format(s, "mod_nw_src:"IP_FMT, IP_ARGS(&a->nw_addr.nw_addr));
> -        break;
> -
> -    case OFPUTIL_OFPAT10_SET_NW_DST:
> -        ds_put_format(s, "mod_nw_dst:"IP_FMT, IP_ARGS(&a->nw_addr.nw_addr));
> -        break;
> -
> -    case OFPUTIL_OFPAT10_SET_NW_TOS:
> -        ds_put_format(s, "mod_nw_tos:%d", a->nw_tos.nw_tos);
> -        break;
> -
> -    case OFPUTIL_OFPAT10_SET_TP_SRC:
> -        ds_put_format(s, "mod_tp_src:%d", ntohs(a->tp_port.tp_port));
> -        break;
> -
> -    case OFPUTIL_OFPAT10_SET_TP_DST:
> -        ds_put_format(s, "mod_tp_dst:%d", ntohs(a->tp_port.tp_port));
> -        break;
> -
> -    case OFPUTIL_NXAST_RESUBMIT:
> -        nar = (struct nx_action_resubmit *)a;
> -        ds_put_format(s, "resubmit:");
> -        ofputil_format_port(ntohs(nar->in_port), s);
> -        break;
> -
> -    case OFPUTIL_NXAST_RESUBMIT_TABLE:
> -        nar = (struct nx_action_resubmit *)a;
> -        ds_put_format(s, "resubmit(");
> -        if (nar->in_port != htons(OFPP_IN_PORT)) {
> -            ofputil_format_port(ntohs(nar->in_port), s);
> -        }
> -        ds_put_char(s, ',');
> -        if (nar->table != 255) {
> -            ds_put_format(s, "%"PRIu8, nar->table);
> -        }
> -        ds_put_char(s, ')');
> -        break;
> -
> -    case OFPUTIL_NXAST_SET_TUNNEL:
> -        nast = (struct nx_action_set_tunnel *)a;
> -        ds_put_format(s, "set_tunnel:%#"PRIx32, ntohl(nast->tun_id));
> -        break;
> -
> -    case OFPUTIL_NXAST_SET_QUEUE:
> -        nasq = (struct nx_action_set_queue *)a;
> -        ds_put_format(s, "set_queue:%u", ntohl(nasq->queue_id));
> -        break;
> -
> -    case OFPUTIL_NXAST_POP_QUEUE:
> -        ds_put_cstr(s, "pop_queue");
> -        break;
> -
> -    case OFPUTIL_NXAST_NOTE:
> -        print_note(s, (const struct nx_action_note *) a);
> -        break;
> -
> -    case OFPUTIL_NXAST_REG_MOVE:
> -        move = (const struct nx_action_reg_move *) a;
> -        nxm_format_reg_move(move, s);
> -        break;
> -
> -    case OFPUTIL_NXAST_REG_LOAD:
> -        load = (const struct nx_action_reg_load *) a;
> -        nxm_format_reg_load(load, s);
> -        break;
> -
> -    case OFPUTIL_NXAST_SET_TUNNEL64:
> -        nast64 = (const struct nx_action_set_tunnel64 *) a;
> -        ds_put_format(s, "set_tunnel64:%#"PRIx64,
> -                      ntohll(nast64->tun_id));
> -        break;
> -
> -    case OFPUTIL_NXAST_MULTIPATH:
> -        nam = (const struct nx_action_multipath *) a;
> -        multipath_format(nam, s);
> -        break;
> -
> -    case OFPUTIL_NXAST_AUTOPATH:
> -        naa = (const struct nx_action_autopath *)a;
> -        ds_put_format(s, "autopath(%u,", ntohl(naa->id));
> -        nxm_decode(&subfield, naa->dst, naa->ofs_nbits);
> -        mf_format_subfield(&subfield, s);
> -        ds_put_char(s, ')');
> -        break;
> -
> -    case OFPUTIL_NXAST_BUNDLE:
> -    case OFPUTIL_NXAST_BUNDLE_LOAD:
> -        bundle_format((const struct nx_action_bundle *) a, s);
> -        break;
> -
> -    case OFPUTIL_NXAST_OUTPUT_REG:
> -        naor = (const struct nx_action_output_reg *) a;
> -        ds_put_cstr(s, "output:");
> -        nxm_decode(&subfield, naor->src, naor->ofs_nbits);
> -        mf_format_subfield(&subfield, s);
> -        break;
> -
> -    case OFPUTIL_NXAST_LEARN:
> -        learn_format((const struct nx_action_learn *) a, s);
> -        break;
> -
> -    case OFPUTIL_NXAST_DEC_TTL:
> -        ds_put_cstr(s, "dec_ttl");
> -        break;
> -
> -    case OFPUTIL_NXAST_EXIT:
> -        ds_put_cstr(s, "exit");
> -        break;
> -
> -    case OFPUTIL_NXAST_FIN_TIMEOUT:
> -        naft = (const struct nx_action_fin_timeout *) a;
> -        ds_put_cstr(s, "fin_timeout(");
> -        if (naft->fin_idle_timeout) {
> -            ds_put_format(s, "idle_timeout=%"PRIu16",",
> -                          ntohs(naft->fin_idle_timeout));
> -        }
> -        if (naft->fin_hard_timeout) {
> -            ds_put_format(s, "hard_timeout=%"PRIu16",",
> -                          ntohs(naft->fin_hard_timeout));
> -        }
> -        ds_chomp(s, ',');
> -        ds_put_char(s, ')');
> -        break;
> -
> -    case OFPUTIL_NXAST_CONTROLLER:
> -        nac = (const struct nx_action_controller *) a;
> -        ds_put_cstr(s, "controller(");
> -        if (nac->reason != OFPR_ACTION) {
> -            ds_put_format(s, "reason=%s,",
> -                          ofputil_packet_in_reason_to_string(nac->reason));
> -        }
> -        if (nac->max_len != htons(UINT16_MAX)) {
> -            ds_put_format(s, "max_len=%"PRIu16",", ntohs(nac->max_len));
> -        }
> -        if (nac->controller_id != htons(0)) {
> -            ds_put_format(s, "id=%"PRIu16",", ntohs(nac->controller_id));
> -        }
> -        ds_chomp(s, ',');
> -        ds_put_char(s, ')');
> -        break;
> -
> -    default:
> -        break;
> -    }
> -}
> -
> -void
> -ofp_print_actions(struct ds *string, const union ofp_action *actions,
> -                  size_t n_actions)
> -{
> -    const union ofp_action *a;
> -    size_t left;
> -
> -    ds_put_cstr(string, "actions=");
> -    if (!n_actions) {
> -        ds_put_cstr(string, "drop");
> -    }
> -
> -    OFPUTIL_ACTION_FOR_EACH (a, left, actions, n_actions) {
> -        int code = ofputil_decode_action(a);
> -        if (code >= 0) {
> -            if (a != actions) {
> -                ds_put_cstr(string, ",");
> -            }
> -            ofp_print_action(string, a, code);
> -        } else {
> -            ofp_print_error(string, -code);
> -        }
> -    }
> -    if (left > 0) {
> -        ds_put_format(string, " ***%zu leftover bytes following actions",
> -                      left * sizeof *a);
> -    }
> -}
> -
> -static void
>  ofp_print_packet_out(struct ds *string, const struct ofp_packet_out *opo,
>                       int verbosity)
>  {
>      struct ofputil_packet_out po;
> +    struct ofpbuf ofpacts;
>      enum ofperr error;
>
> -    error = ofputil_decode_packet_out(&po, opo);
> +    ofpbuf_init(&ofpacts, 64);
> +    error = ofputil_decode_packet_out(&po, opo, &ofpacts);
>      if (error) {
> +        ofpbuf_uninit(&ofpacts);
>          ofp_print_error(string, error);
>          return;
>      }
> @@ -434,7 +174,7 @@ ofp_print_packet_out(struct ds *string, const struct ofp_packet_out *opo,
>      ofputil_format_port(po.in_port, string);
>
>      ds_put_char(string, ' ');
> -    ofp_print_actions(string, po.actions, po.n_actions);
> +    ofpacts_format(po.ofpacts, string);
>
>      if (po.buffer_id == UINT32_MAX) {
>          ds_put_format(string, " data_len=%zu", po.packet_len);
> @@ -448,6 +188,8 @@ ofp_print_packet_out(struct ds *string, const struct ofp_packet_out *opo,
>          ds_put_format(string, " buffer=0x%08"PRIx32, po.buffer_id);
>      }
>      ds_put_char(string, '\n');
> +
> +    ofpbuf_uninit(&ofpacts);
>  }
>
>  /* qsort comparison function. */
> @@ -928,11 +670,14 @@ ofp_print_flow_mod(struct ds *s, const struct ofp_header *oh,
>                     enum ofputil_msg_code code, int verbosity)
>  {
>      struct ofputil_flow_mod fm;
> +    struct ofpbuf ofpacts;
>      bool need_priority;
>      enum ofperr error;
>
> -    error = ofputil_decode_flow_mod(&fm, oh, OFPUTIL_P_OF10_TID);
> +    ofpbuf_init(&ofpacts, 64);
> +    error = ofputil_decode_flow_mod(&fm, oh, OFPUTIL_P_OF10_TID, &ofpacts);
>      if (error) {
> +        ofpbuf_uninit(&ofpacts);
>          ofp_print_error(s, error);
>          return;
>      }
> @@ -1027,7 +772,8 @@ ofp_print_flow_mod(struct ds *s, const struct ofp_header *oh,
>          }
>      }
>
> -    ofp_print_actions(s, fm.actions, fm.n_actions);
> +    ofpacts_format(fm.ofpacts, s);
> +    ofpbuf_uninit(&ofpacts);
>  }
>
>  static void
> @@ -1229,14 +975,16 @@ ofp_print_flow_stats_request(struct ds *string,
>  static void
>  ofp_print_flow_stats_reply(struct ds *string, const struct ofp_header *oh)
>  {
> +    struct ofpbuf ofpacts;
>      struct ofpbuf b;
>
>      ofpbuf_use_const(&b, oh, ntohs(oh->length));
> +    ofpbuf_init(&ofpacts, 64);
>      for (;;) {
>          struct ofputil_flow_stats fs;
>          int retval;
>
> -        retval = ofputil_decode_flow_stats_reply(&fs, &b, true);
> +        retval = ofputil_decode_flow_stats_reply(&fs, &b, true, &ofpacts);
>          if (retval) {
>              if (retval != EOF) {
>                  ds_put_cstr(string, " ***parse error***");
> @@ -1269,8 +1017,9 @@ ofp_print_flow_stats_reply(struct ds *string, const struct ofp_header *oh)
>          if (string->string[string->length - 1] != ' ') {
>              ds_put_char(string, ' ');
>          }
> -        ofp_print_actions(string, fs.actions, fs.n_actions);
> -     }
> +        ofpacts_format(fs.ofpacts, string);
> +    }
> +    ofpbuf_uninit(&ofpacts);
>  }
>
>  static void
> diff --git a/lib/ofp-print.h b/lib/ofp-print.h
> index ae868a4..49bcfcd 100644
> --- a/lib/ofp-print.h
> +++ b/lib/ofp-print.h
> @@ -25,7 +25,6 @@
>  struct ofp_flow_mod;
>  struct ofp10_match;
>  struct ds;
> -union ofp_action;
>
>  #ifdef  __cplusplus
>  extern "C" {
> @@ -34,7 +33,6 @@ extern "C" {
>  void ofp_print(FILE *, const void *, size_t, int verbosity);
>  void ofp_print_packet(FILE *stream, const void *data, size_t len);
>
> -void ofp_print_actions(struct ds *, const union ofp_action *, size_t);
>  void ofp10_match_print(struct ds *, const struct ofp10_match *, int verbosity);
>
>  char *ofp_to_string(const void *, size_t, int verbosity);
> diff --git a/lib/ofp-util.c b/lib/ofp-util.c
> index cebc523..e31a304 100644
> --- a/lib/ofp-util.c
> +++ b/lib/ofp-util.c
> @@ -32,6 +32,7 @@
>  #include "multipath.h"
>  #include "netdev.h"
>  #include "nx-match.h"
> +#include "ofp-actions.h"
>  #include "ofp-errors.h"
>  #include "ofp-util.h"
>  #include "ofpbuf.h"
> @@ -1644,11 +1645,17 @@ ofputil_make_flow_mod_table_id(bool flow_mod_table_id)
>   * flow_mod in 'fm'.  Returns 0 if successful, otherwise an OpenFlow error
>   * code.
>   *
> - * Does not validate the flow_mod actions. */
> + * Uses 'ofpacts' to store the abstract OFPACT_* version of 'oh''s actions.
> + * The caller must initialize 'ofpacts' and retains ownership of it.
> + * 'fm->ofpacts' will point into the 'ofpacts' buffer.
> + *
> + * Does not validate the flow_mod actions.  The caller should do that, with
> + * ofpacts_check(). */
>  enum ofperr
>  ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
>                          const struct ofp_header *oh,
> -                        enum ofputil_protocol protocol)
> +                        enum ofputil_protocol protocol,
> +                        struct ofpbuf *ofpacts)
>  {
>      const struct ofputil_msg_type *type;
>      uint16_t command;
> @@ -1663,12 +1670,8 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
>          uint16_t priority;
>          enum ofperr error;
>
> -        /* Dissect the message. */
> +        /* Get the ofp_flow_mod. */
>          ofm = ofpbuf_pull(&b, sizeof *ofm);
> -        error = ofputil_pull_actions(&b, b.size, &fm->actions, &fm->n_actions);
> -        if (error) {
> -            return error;
> -        }
>
>          /* Set priority based on original wildcards.  Normally we'd allow
>           * ofputil_cls_rule_from_match() to do this for us, but
> @@ -1683,6 +1686,12 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
>          ofputil_cls_rule_from_ofp10_match(&ofm->match, priority, &fm->cr);
>          ofputil_normalize_rule(&fm->cr);
>
> +        /* Now get the actions. */
> +        error = ofpacts_pull_openflow(&b, b.size, ofpacts);
> +        if (error) {
> +            return error;
> +        }
> +
>          /* Translate the message. */
>          command = ntohs(ofm->command);
>          fm->cookie = htonll(0);
> @@ -1705,7 +1714,7 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
>          if (error) {
>              return error;
>          }
> -        error = ofputil_pull_actions(&b, b.size, &fm->actions, &fm->n_actions);
> +        error = ofpacts_pull_openflow(&b, b.size, ofpacts);
>          if (error) {
>              return error;
>          }
> @@ -1727,6 +1736,8 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
>          NOT_REACHED();
>      }
>
> +    fm->ofpacts = ofpacts->data;
> +    fm->ofpacts_len = ofpacts->size;
>      if (protocol & OFPUTIL_P_TID) {
>          fm->command = command & 0xff;
>          fm->table_id = command >> 8;
> @@ -1744,7 +1755,6 @@ struct ofpbuf *
>  ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm,
>                          enum ofputil_protocol protocol)
>  {
> -    size_t actions_len = fm->n_actions * sizeof *fm->actions;
>      struct ofp_flow_mod *ofm;
>      struct nx_flow_mod *nfm;
>      struct ofpbuf *msg;
> @@ -1758,7 +1768,7 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm,
>      switch (protocol) {
>      case OFPUTIL_P_OF10:
>      case OFPUTIL_P_OF10_TID:
> -        msg = ofpbuf_new(sizeof *ofm + actions_len);
> +        msg = ofpbuf_new(sizeof *ofm + fm->ofpacts_len);
>          ofm = put_openflow(sizeof *ofm, OFPT10_FLOW_MOD, msg);
>          ofputil_cls_rule_to_ofp10_match(&fm->cr, &ofm->match);
>          ofm->cookie = fm->new_cookie;
> @@ -1773,7 +1783,7 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm,
>
>      case OFPUTIL_P_NXM:
>      case OFPUTIL_P_NXM_TID:
> -        msg = ofpbuf_new(sizeof *nfm + NXM_TYPICAL_LEN + actions_len);
> +        msg = ofpbuf_new(sizeof *nfm + NXM_TYPICAL_LEN + fm->ofpacts_len);
>          put_nxmsg(sizeof *nfm, NXT_FLOW_MOD, msg);
>          nfm = msg->data;
>          nfm->command = htons(command);
> @@ -1794,7 +1804,9 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm,
>          NOT_REACHED();
>      }
>
> -    ofpbuf_put(msg, fm->actions, actions_len);
> +    if (fm->ofpacts) {
> +        ofpacts_to_openflow(fm->ofpacts, msg);
> +    }
>      update_openflow_length(msg);
>      return msg;
>  }
> @@ -1988,12 +2000,17 @@ ofputil_flow_stats_request_usable_protocols(
>   * 'flow_age_extension' as true so that the contents of 'msg' determine the
>   * 'idle_age' and 'hard_age' members in 'fs'.
>   *
> + * Uses 'ofpacts' to store the abstract OFPACT_* version of the flow stats
> + * reply's actions.  The caller must initialize 'ofpacts' and retains ownership
> + * of it.  'fs->ofpacts' will point into the 'ofpacts' buffer.
> + *
>   * Returns 0 if successful, EOF if no replies were left in this 'msg',
>   * otherwise a positive errno value. */
>  int
>  ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs,
>                                  struct ofpbuf *msg,
> -                                bool flow_age_extension)
> +                                bool flow_age_extension,
> +                                struct ofpbuf *ofpacts)
>  {
>      const struct ofputil_msg_type *type;
>      int code;
> @@ -2031,8 +2048,7 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs,
>              return EINVAL;
>          }
>
> -        if (ofputil_pull_actions(msg, length - sizeof *ofs,
> -                                 &fs->actions, &fs->n_actions)) {
> +        if (ofpacts_pull_openflow(msg, length - sizeof *ofs, ofpacts)) {
>              return EINVAL;
>          }
>
> @@ -2050,7 +2066,7 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs,
>          fs->byte_count = ntohll(get_32aligned_be64(&ofs->byte_count));
>      } else if (code == OFPUTIL_NXST_FLOW_REPLY) {
>          const struct nx_flow_stats *nfs;
> -        size_t match_len, length;
> +        size_t match_len, actions_len, length;
>
>          nfs = ofpbuf_try_pull(msg, sizeof *nfs);
>          if (!nfs) {
> @@ -2071,9 +2087,8 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs,
>              return EINVAL;
>          }
>
> -        if (ofputil_pull_actions(msg,
> -                                 length - sizeof *nfs - ROUND_UP(match_len, 8),
> -                                 &fs->actions, &fs->n_actions)) {
> +        actions_len = length - sizeof *nfs - ROUND_UP(match_len, 8);
> +        if (ofpacts_pull_openflow(msg, actions_len, ofpacts)) {
>              return EINVAL;
>          }
>
> @@ -2099,6 +2114,9 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs,
>          NOT_REACHED();
>      }
>
> +    fs->ofpacts = ofpacts->data;
> +    fs->ofpacts_len = ofpacts->size;
> +
>      return 0;
>  }
>
> @@ -2119,16 +2137,14 @@ void
>  ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs,
>                                  struct list *replies)
>  {
> -    size_t act_len = fs->n_actions * sizeof *fs->actions;
> -    const struct ofp_stats_msg *osm;
> +    struct ofpbuf *reply = ofpbuf_from_list(list_back(replies));
> +    const struct ofp_stats_msg *osm = reply->data;
> +    size_t start_ofs = reply->size;
>
> -    osm = ofpbuf_from_list(list_back(replies))->data;
>      if (osm->type == htons(OFPST_FLOW)) {
> -        size_t len = offsetof(struct ofp_flow_stats, actions) + act_len;
>          struct ofp_flow_stats *ofs;
>
> -        ofs = ofputil_append_stats_reply(len, replies);
> -        ofs->length = htons(len);
> +        ofs = ofpbuf_put_uninit(reply, sizeof *ofs);
>          ofs->table_id = fs->table_id;
>          ofs->pad = 0;
>          ofputil_cls_rule_to_ofp10_match(&fs->rule, &ofs->match);
> @@ -2143,17 +2159,14 @@ ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs,
>                             htonll(unknown_to_zero(fs->packet_count)));
>          put_32aligned_be64(&ofs->byte_count,
>                             htonll(unknown_to_zero(fs->byte_count)));
> -        memcpy(ofs->actions, fs->actions, act_len);
> +        ofpacts_to_openflow(fs->ofpacts, reply);
> +
> +        ofs = ofpbuf_at_assert(reply, start_ofs, sizeof *ofs);
> +        ofs->length = htons(reply->size - start_ofs);
>      } else if (osm->type == htons(OFPST_VENDOR)) {
>          struct nx_flow_stats *nfs;
> -        struct ofpbuf *msg;
> -        size_t start_len;
>
> -        msg = ofputil_reserve_stats_reply(
> -            sizeof *nfs + NXM_MAX_LEN + act_len, replies);
> -        start_len = msg->size;
> -
> -        nfs = ofpbuf_put_uninit(msg, sizeof *nfs);
> +        nfs = ofpbuf_put_uninit(reply, sizeof *nfs);
>          nfs->table_id = fs->table_id;
>          nfs->pad = 0;
>          nfs->duration_sec = htonl(fs->duration_sec);
> @@ -2167,15 +2180,19 @@ ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs,
>          nfs->hard_age = htons(fs->hard_age < 0 ? 0
>                                : fs->hard_age < UINT16_MAX ? fs->hard_age + 1
>                                : UINT16_MAX);
> -        nfs->match_len = htons(nx_put_match(msg, false, &fs->rule, 0, 0));
> +        nfs->match_len = htons(nx_put_match(reply, false, &fs->rule, 0, 0));
>          nfs->cookie = fs->cookie;
>          nfs->packet_count = htonll(fs->packet_count);
>          nfs->byte_count = htonll(fs->byte_count);
> -        ofpbuf_put(msg, fs->actions, act_len);
> -        nfs->length = htons(msg->size - start_len);
> +        ofpacts_to_openflow(fs->ofpacts, reply);
> +
> +        nfs = ofpbuf_at_assert(reply, start_ofs, sizeof *nfs);
> +        nfs->length = htons(reply->size - start_ofs);
>      } else {
>          NOT_REACHED();
>      }
> +
> +    ofputil_postappend_stats_reply(start_ofs, replies);
>  }
>
>  /* Converts abstract ofputil_aggregate_stats 'stats' into an OFPST_AGGREGATE or
> @@ -2501,9 +2518,18 @@ ofputil_packet_in_reason_from_string(const char *s,
>      return false;
>  }
>
> +/* Converts an OFPT_PACKET_OUT in 'opo' into an abstract ofputil_packet_out in
> + * 'po'.
> + *
> + * Uses 'ofpacts' to store the abstract OFPACT_* version of the packet out
> + * message's actions.  The caller must initialize 'ofpacts' and retains
> + * ownership of it.  'po->ofpacts' will point into the 'ofpacts' buffer.
> + *
> + * Returns 0 if successful, otherwise an OFPERR_* value. */
>  enum ofperr
>  ofputil_decode_packet_out(struct ofputil_packet_out *po,
> -                          const struct ofp_packet_out *opo)
> +                          const struct ofp_packet_out *opo,
> +                          struct ofpbuf *ofpacts)
>  {
>      enum ofperr error;
>      struct ofpbuf b;
> @@ -2520,11 +2546,12 @@ ofputil_decode_packet_out(struct ofputil_packet_out *po,
>      ofpbuf_use_const(&b, opo, ntohs(opo->header.length));
>      ofpbuf_pull(&b, sizeof *opo);
>
> -    error = ofputil_pull_actions(&b, ntohs(opo->actions_len),
> -                                 &po->actions, &po->n_actions);
> +    error = ofpacts_pull_openflow(&b, ntohs(opo->actions_len), ofpacts);
>      if (error) {
>          return error;
>      }
> +    po->ofpacts = ofpacts->data;
> +    po->ofpacts_len = ofpacts->size;
>
>      if (po->buffer_id == UINT32_MAX) {
>          po->packet = b.data;
> @@ -3070,25 +3097,27 @@ struct ofpbuf *
>  ofputil_encode_packet_out(const struct ofputil_packet_out *po)
>  {
>      struct ofp_packet_out *opo;
> -    size_t actions_len;
>      struct ofpbuf *msg;
>      size_t size;
>
> -    actions_len = po->n_actions * sizeof *po->actions;
> -    size = sizeof *opo + actions_len;
> +    size = sizeof *opo + po->ofpacts_len;
>      if (po->buffer_id == UINT32_MAX) {
>          size += po->packet_len;
>      }
>
>      msg = ofpbuf_new(size);
> -    opo = put_openflow(sizeof *opo, OFPT10_PACKET_OUT, msg);
> +    put_openflow(sizeof *opo, OFPT10_PACKET_OUT, msg);
> +    ofpacts_to_openflow(po->ofpacts, msg);
> +
> +    opo = msg->data;
>      opo->buffer_id = htonl(po->buffer_id);
>      opo->in_port = htons(po->in_port);
> -    opo->actions_len = htons(actions_len);
> -    ofpbuf_put(msg, po->actions, actions_len);
> +    opo->actions_len = htons(msg->size - sizeof *opo);
> +
>      if (po->buffer_id == UINT32_MAX) {
>          ofpbuf_put(msg, po->packet, po->packet_len);
>      }
> +
>      update_openflow_length(msg);
>
>      return msg;
> @@ -3362,6 +3391,20 @@ ofputil_append_stats_reply(size_t len, struct list *replies)
>      return ofpbuf_put_uninit(ofputil_reserve_stats_reply(len, replies), len);
>  }
>
> +void
> +ofputil_postappend_stats_reply(size_t start_ofs, struct list *replies)
> +{
> +    struct ofpbuf *msg = ofpbuf_from_list(list_back(replies));
> +
> +    assert(start_ofs <= UINT16_MAX);
> +    if (msg->size > UINT16_MAX) {
> +        size_t len = msg->size - start_ofs;
> +        memcpy(ofputil_append_stats_reply(len, replies),
> +               (const uint8_t *) msg->data + start_ofs, len);
> +        msg->size = start_ofs;
> +    }
> +}
> +
>  /* Returns the first byte past the ofp_stats_msg header in 'oh'. */
>  const void *
>  ofputil_stats_body(const struct ofp_header *oh)
> @@ -3661,280 +3704,6 @@ size_t ofputil_count_phy_ports(uint8_t ofp_version, struct ofpbuf *b)
>      return b->size / ofputil_get_phy_port_size(ofp_version);
>  }
>
> -static enum ofperr
> -check_resubmit_table(const struct nx_action_resubmit *nar)
> -{
> -    if (nar->pad[0] || nar->pad[1] || nar->pad[2]) {
> -        return OFPERR_OFPBAC_BAD_ARGUMENT;
> -    }
> -    return 0;
> -}
> -
> -static enum ofperr
> -check_output_reg(const struct nx_action_output_reg *naor,
> -                 const struct flow *flow)
> -{
> -    struct mf_subfield src;
> -    size_t i;
> -
> -    for (i = 0; i < sizeof naor->zero; i++) {
> -        if (naor->zero[i]) {
> -            return OFPERR_OFPBAC_BAD_ARGUMENT;
> -        }
> -    }
> -
> -    nxm_decode(&src, naor->src, naor->ofs_nbits);
> -    return mf_check_src(&src, flow);
> -}
> -
> -enum ofperr
> -validate_actions(const union ofp_action *actions, size_t n_actions,
> -                 const struct flow *flow, int max_ports)
> -{
> -    const union ofp_action *a;
> -    size_t left;
> -
> -    OFPUTIL_ACTION_FOR_EACH (a, left, actions, n_actions) {
> -        enum ofperr error;
> -        uint16_t port;
> -        int code;
> -
> -        code = ofputil_decode_action(a);
> -        if (code < 0) {
> -            error = -code;
> -            VLOG_WARN_RL(&bad_ofmsg_rl,
> -                         "action decoding error at offset %td (%s)",
> -                         (a - actions) * sizeof *a, ofperr_get_name(error));
> -
> -            return error;
> -        }
> -
> -        error = 0;
> -        switch ((enum ofputil_action_code) code) {
> -        case OFPUTIL_ACTION_INVALID:
> -            NOT_REACHED();
> -
> -        case OFPUTIL_OFPAT10_OUTPUT:
> -            error = ofputil_check_output_port(ntohs(a->output.port),
> -                                              max_ports);
> -            break;
> -
> -        case OFPUTIL_OFPAT10_SET_VLAN_VID:
> -            if (a->vlan_vid.vlan_vid & ~htons(0xfff)) {
> -                error = OFPERR_OFPBAC_BAD_ARGUMENT;
> -            }
> -            break;
> -
> -        case OFPUTIL_OFPAT10_SET_VLAN_PCP:
> -            if (a->vlan_pcp.vlan_pcp & ~7) {
> -                error = OFPERR_OFPBAC_BAD_ARGUMENT;
> -            }
> -            break;
> -
> -        case OFPUTIL_OFPAT10_ENQUEUE:
> -            port = ntohs(((const struct ofp_action_enqueue *) a)->port);
> -            if (port >= max_ports && port != OFPP_IN_PORT
> -                && port != OFPP_LOCAL) {
> -                error = OFPERR_OFPBAC_BAD_OUT_PORT;
> -            }
> -            break;
> -
> -        case OFPUTIL_NXAST_REG_MOVE:
> -            error = nxm_check_reg_move((const struct nx_action_reg_move *) a,
> -                                       flow);
> -            break;
> -
> -        case OFPUTIL_NXAST_REG_LOAD:
> -            error = nxm_check_reg_load((const struct nx_action_reg_load *) a,
> -                                       flow);
> -            break;
> -
> -        case OFPUTIL_NXAST_MULTIPATH:
> -            error = multipath_check((const struct nx_action_multipath *) a,
> -                                    flow);
> -            break;
> -
> -        case OFPUTIL_NXAST_AUTOPATH:
> -            error = autopath_check((const struct nx_action_autopath *) a,
> -                                   flow);
> -            break;
> -
> -        case OFPUTIL_NXAST_BUNDLE:
> -        case OFPUTIL_NXAST_BUNDLE_LOAD:
> -            error = bundle_check((const struct nx_action_bundle *) a,
> -                                 max_ports, flow);
> -            break;
> -
> -        case OFPUTIL_NXAST_OUTPUT_REG:
> -            error = check_output_reg((const struct nx_action_output_reg *) a,
> -                                     flow);
> -            break;
> -
> -        case OFPUTIL_NXAST_RESUBMIT_TABLE:
> -            error = check_resubmit_table(
> -                (const struct nx_action_resubmit *) a);
> -            break;
> -
> -        case OFPUTIL_NXAST_LEARN:
> -            error = learn_check((const struct nx_action_learn *) a, flow);
> -            break;
> -
> -        case OFPUTIL_NXAST_CONTROLLER:
> -            if (((const struct nx_action_controller *) a)->zero) {
> -                error = OFPERR_NXBAC_MUST_BE_ZERO;
> -            }
> -            break;
> -
> -        case OFPUTIL_OFPAT10_STRIP_VLAN:
> -        case OFPUTIL_OFPAT10_SET_NW_SRC:
> -        case OFPUTIL_OFPAT10_SET_NW_DST:
> -        case OFPUTIL_OFPAT10_SET_NW_TOS:
> -        case OFPUTIL_OFPAT10_SET_TP_SRC:
> -        case OFPUTIL_OFPAT10_SET_TP_DST:
> -        case OFPUTIL_OFPAT10_SET_DL_SRC:
> -        case OFPUTIL_OFPAT10_SET_DL_DST:
> -        case OFPUTIL_NXAST_RESUBMIT:
> -        case OFPUTIL_NXAST_SET_TUNNEL:
> -        case OFPUTIL_NXAST_SET_QUEUE:
> -        case OFPUTIL_NXAST_POP_QUEUE:
> -        case OFPUTIL_NXAST_NOTE:
> -        case OFPUTIL_NXAST_SET_TUNNEL64:
> -        case OFPUTIL_NXAST_EXIT:
> -        case OFPUTIL_NXAST_DEC_TTL:
> -        case OFPUTIL_NXAST_FIN_TIMEOUT:
> -            break;
> -        }
> -
> -        if (error) {
> -            VLOG_WARN_RL(&bad_ofmsg_rl, "bad action at offset %td (%s)",
> -                         (a - actions) * sizeof *a, ofperr_get_name(error));
> -            return error;
> -        }
> -    }
> -    if (left) {
> -        VLOG_WARN_RL(&bad_ofmsg_rl, "bad action format at offset %zu",
> -                     (n_actions - left) * sizeof *a);
> -        return OFPERR_OFPBAC_BAD_LEN;
> -    }
> -    return 0;
> -}
> -
> -struct ofputil_action {
> -    int code;
> -    unsigned int min_len;
> -    unsigned int max_len;
> -};
> -
> -static const struct ofputil_action action_bad_type
> -    = { -OFPERR_OFPBAC_BAD_TYPE,   0, UINT_MAX };
> -static const struct ofputil_action action_bad_len
> -    = { -OFPERR_OFPBAC_BAD_LEN,    0, UINT_MAX };
> -static const struct ofputil_action action_bad_vendor
> -    = { -OFPERR_OFPBAC_BAD_VENDOR, 0, UINT_MAX };
> -
> -static const struct ofputil_action *
> -ofputil_decode_ofpat_action(const union ofp_action *a)
> -{
> -    enum ofp10_action_type type = ntohs(a->type);
> -
> -    switch (type) {
> -#define OFPAT10_ACTION(ENUM, STRUCT, NAME)                    \
> -        case ENUM: {                                        \
> -            static const struct ofputil_action action = {   \
> -                OFPUTIL_##ENUM,                             \
> -                sizeof(struct STRUCT),                      \
> -                sizeof(struct STRUCT)                       \
> -            };                                              \
> -            return &action;                                 \
> -        }
> -#include "ofp-util.def"
> -
> -    case OFPAT10_VENDOR:
> -    default:
> -        return &action_bad_type;
> -    }
> -}
> -
> -static const struct ofputil_action *
> -ofputil_decode_nxast_action(const union ofp_action *a)
> -{
> -    const struct nx_action_header *nah = (const struct nx_action_header *) a;
> -    enum nx_action_subtype subtype = ntohs(nah->subtype);
> -
> -    switch (subtype) {
> -#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME)            \
> -        case ENUM: {                                            \
> -            static const struct ofputil_action action = {       \
> -                OFPUTIL_##ENUM,                                 \
> -                sizeof(struct STRUCT),                          \
> -                EXTENSIBLE ? UINT_MAX : sizeof(struct STRUCT)   \
> -            };                                                  \
> -            return &action;                                     \
> -        }
> -#include "ofp-util.def"
> -
> -    case NXAST_SNAT__OBSOLETE:
> -    case NXAST_DROP_SPOOFED_ARP__OBSOLETE:
> -    default:
> -        return &action_bad_type;
> -    }
> -}
> -
> -/* Parses 'a' to determine its type.  Returns a nonnegative OFPUTIL_OFPAT10_* or
> - * OFPUTIL_NXAST_* constant if successful, otherwise a negative OFPERR_* error
> - * code.
> - *
> - * The caller must have already verified that 'a''s length is correct (that is,
> - * a->header.len is nonzero and a multiple of sizeof(union ofp_action) and no
> - * longer than the amount of space allocated to 'a').
> - *
> - * This function verifies that 'a''s length is correct for the type of action
> - * that it represents. */
> -int
> -ofputil_decode_action(const union ofp_action *a)
> -{
> -    const struct ofputil_action *action;
> -    uint16_t len = ntohs(a->header.len);
> -
> -    if (a->type != htons(OFPAT10_VENDOR)) {
> -        action = ofputil_decode_ofpat_action(a);
> -    } else {
> -        switch (ntohl(a->vendor.vendor)) {
> -        case NX_VENDOR_ID:
> -            if (len < sizeof(struct nx_action_header)) {
> -                return -OFPERR_OFPBAC_BAD_LEN;
> -            }
> -            action = ofputil_decode_nxast_action(a);
> -            break;
> -        default:
> -            action = &action_bad_vendor;
> -            break;
> -        }
> -    }
> -
> -    return (len >= action->min_len && len <= action->max_len
> -            ? action->code
> -            : -OFPERR_OFPBAC_BAD_LEN);
> -}
> -
> -/* Parses 'a' and returns its type as an OFPUTIL_OFPAT10_* or OFPUTIL_NXAST_*
> - * constant.  The caller must have already validated that 'a' is a valid action
> - * understood by Open vSwitch (e.g. by a previous successful call to
> - * ofputil_decode_action()). */
> -enum ofputil_action_code
> -ofputil_decode_action_unsafe(const union ofp_action *a)
> -{
> -    const struct ofputil_action *action;
> -
> -    if (a->type != htons(OFPAT10_VENDOR)) {
> -        action = ofputil_decode_ofpat_action(a);
> -    } else {
> -        action = ofputil_decode_nxast_action(a);
> -    }
> -
> -    return action->code;
> -}
> -
>  /* Returns the 'enum ofputil_action_code' corresponding to 'name' (e.g. if
>   * 'name' is "output" then the return value is OFPUTIL_OFPAT10_OUTPUT), or -1 if
>   * 'name' is not the name of any action.
> @@ -4017,22 +3786,6 @@ ofputil_put_action(enum ofputil_action_code code, struct ofpbuf *buf)
>      }
>  #include "ofp-util.def"
>
> -/* Returns true if 'action' outputs to 'port', false otherwise. */
> -bool
> -action_outputs_to_port(const union ofp_action *action, ovs_be16 port)
> -{
> -    switch (ofputil_decode_action(action)) {
> -    case OFPUTIL_OFPAT10_OUTPUT:
> -        return action->output.port == port;
> -    case OFPUTIL_OFPAT10_ENQUEUE:
> -        return ((const struct ofp_action_enqueue *) action)->port == port;
> -    case OFPUTIL_NXAST_CONTROLLER:
> -        return port == htons(OFPP_CONTROLLER);
> -    default:
> -        return false;
> -    }
> -}
> -
>  /* "Normalizes" the wildcards in 'rule'.  That means:
>   *
>   *    1. If the type of level N is known, then only the valid fields for that
> @@ -4140,57 +3893,6 @@ ofputil_normalize_rule(struct cls_rule *rule)
>      }
>  }
>
> -/* Attempts to pull 'actions_len' bytes from the front of 'b'.  Returns 0 if
> - * successful, otherwise an OpenFlow error.
> - *
> - * If successful, the first action is stored in '*actionsp' and the number of
> - * "union ofp_action" size elements into '*n_actionsp'.  Otherwise NULL and 0
> - * are stored, respectively.
> - *
> - * This function does not check that the actions are valid (the caller should
> - * do so, with validate_actions()).  The caller is also responsible for making
> - * sure that 'b->data' is initially aligned appropriately for "union
> - * ofp_action". */
> -enum ofperr
> -ofputil_pull_actions(struct ofpbuf *b, unsigned int actions_len,
> -                     union ofp_action **actionsp, size_t *n_actionsp)
> -{
> -    if (actions_len % OFP_ACTION_ALIGN != 0) {
> -        VLOG_WARN_RL(&bad_ofmsg_rl, "OpenFlow message actions length %u "
> -                     "is not a multiple of %d", actions_len, OFP_ACTION_ALIGN);
> -        goto error;
> -    }
> -
> -    *actionsp = ofpbuf_try_pull(b, actions_len);
> -    if (*actionsp == NULL) {
> -        VLOG_WARN_RL(&bad_ofmsg_rl, "OpenFlow message actions length %u "
> -                     "exceeds remaining message length (%zu)",
> -                     actions_len, b->size);
> -        goto error;
> -    }
> -
> -    *n_actionsp = actions_len / OFP_ACTION_ALIGN;
> -    return 0;
> -
> -error:
> -    *actionsp = NULL;
> -    *n_actionsp = 0;
> -    return OFPERR_OFPBRC_BAD_LEN;
> -}
> -
> -bool
> -ofputil_actions_equal(const union ofp_action *a, size_t n_a,
> -                      const union ofp_action *b, size_t n_b)
> -{
> -    return n_a == n_b && (!n_a || !memcmp(a, b, n_a * sizeof *a));
> -}
> -
> -union ofp_action *
> -ofputil_actions_clone(const union ofp_action *actions, size_t n)
> -{
> -    return n ? xmemdup(actions, n * sizeof *actions) : NULL;
> -}
> -
>  /* Parses a key or a key-value pair from '*stringp'.
>   *
>   * On success: Stores the key into '*keyp'.  Stores the value, if present, into
> diff --git a/lib/ofp-util.h b/lib/ofp-util.h
> index 3f9e440..f703c8a 100644
> --- a/lib/ofp-util.h
> +++ b/lib/ofp-util.h
> @@ -22,6 +22,7 @@
>  #include <stddef.h>
>  #include <stdint.h>
>  #include "classifier.h"
> +#include "compiler.h"
>  #include "flow.h"
>  #include "netdev.h"
>  #include "openflow/nicira-ext.h"
> @@ -236,13 +237,14 @@ struct ofputil_flow_mod {
>      uint32_t buffer_id;
>      uint16_t out_port;
>      uint16_t flags;
> -    union ofp_action *actions;
> -    size_t n_actions;
> +    struct ofpact *ofpacts;     /* Series of "struct ofpact"s. */
> +    size_t ofpacts_len;         /* Length of ofpacts, in bytes. */
>  };
>
>  enum ofperr ofputil_decode_flow_mod(struct ofputil_flow_mod *,
>                                      const struct ofp_header *,
> -                                    enum ofputil_protocol);
> +                                    enum ofputil_protocol,
> +                                    struct ofpbuf *ofpacts);
>  struct ofpbuf *ofputil_encode_flow_mod(const struct ofputil_flow_mod *,
>                                         enum ofputil_protocol);
>
> @@ -279,13 +281,14 @@ struct ofputil_flow_stats {
>      int hard_age;               /* Seconds since last change, -1 if unknown. */
>      uint64_t packet_count;      /* Packet count, UINT64_MAX if unknown. */
>      uint64_t byte_count;        /* Byte count, UINT64_MAX if unknown. */
> -    union ofp_action *actions;
> -    size_t n_actions;
> +    struct ofpact *ofpacts;
> +    size_t ofpacts_len;
>  };
>
>  int ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *,
>                                      struct ofpbuf *msg,
> -                                    bool flow_age_extension);
> +                                    bool flow_age_extension,
> +                                    struct ofpbuf *ofpacts);
>  void ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *,
>                                       struct list *replies);
>
> @@ -352,12 +355,13 @@ struct ofputil_packet_out {
>      size_t packet_len;          /* Length of packet data in bytes. */
>      uint32_t buffer_id;         /* Buffer id or UINT32_MAX if no buffer. */
>      uint16_t in_port;           /* Packet's input port. */
> -    union ofp_action *actions;  /* Actions. */
> -    size_t n_actions;           /* Number of elements in 'actions' array. */
> +    struct ofpact *ofpacts;     /* Actions. */
> +    size_t ofpacts_len;         /* Size of ofpacts in bytes. */
>  };
>
>  enum ofperr ofputil_decode_packet_out(struct ofputil_packet_out *,
> -                                      const struct ofp_packet_out *);
> +                                      const struct ofp_packet_out *,
> +                                      struct ofpbuf *ofpacts);
>  struct ofpbuf *ofputil_encode_packet_out(const struct ofputil_packet_out *);
>
>  enum ofputil_port_config {
> @@ -531,6 +535,7 @@ void ofputil_start_stats_reply(const struct ofp_stats_msg *request,
>                                 struct list *);
>  struct ofpbuf *ofputil_reserve_stats_reply(size_t len, struct list *);
>  void *ofputil_append_stats_reply(size_t len, struct list *);
> +void ofputil_postappend_stats_reply(size_t start_ofs, struct list *);
>
>  void ofputil_append_port_desc_stats_reply(uint8_t ofp_version,
>                                            const struct ofputil_phy_port *pp,
> @@ -597,7 +602,7 @@ bool ofputil_frag_handling_from_string(const char *, enum ofp_config_flags *);
>   *
>   * (The above list helps developers who want to "grep" for these definitions.)
>   */
> -enum ofputil_action_code {
> +enum OVS_PACKED_ENUM ofputil_action_code {
>      OFPUTIL_ACTION_INVALID,
>  #define OFPAT10_ACTION(ENUM, STRUCT, NAME)             OFPUTIL_##ENUM,
>  #define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) OFPUTIL_##ENUM,
> @@ -612,10 +617,6 @@ enum {
>  #include "ofp-util.def"
>  };
>
> -int ofputil_decode_action(const union ofp_action *);
> -enum ofputil_action_code ofputil_decode_action_unsafe(
> -    const union ofp_action *);
> -
>  int ofputil_action_code_from_name(const char *);
>
>  void *ofputil_put_action(enum ofputil_action_code, struct ofpbuf *buf);
> @@ -644,38 +645,6 @@ void *ofputil_put_action(enum ofputil_action_code, struct ofpbuf *buf);
>
>  #define OFP_ACTION_ALIGN 8      /* Alignment of ofp_actions. */
>
> -static inline union ofp_action *
> -ofputil_action_next(const union ofp_action *a)
> -{
> -    return ((union ofp_action *) (void *)
> -            ((uint8_t *) a + ntohs(a->header.len)));
> -}
> -
> -static inline bool
> -ofputil_action_is_valid(const union ofp_action *a, size_t n_actions)
> -{
> -    uint16_t len = ntohs(a->header.len);
> -    return (!(len % OFP_ACTION_ALIGN)
> -            && len >= sizeof *a
> -            && len / sizeof *a <= n_actions);
> -}
> -
> -/* This macro is careful to check for actions with bad lengths. */
> -#define OFPUTIL_ACTION_FOR_EACH(ITER, LEFT, ACTIONS, N_ACTIONS)         \
> -    for ((ITER) = (ACTIONS), (LEFT) = (N_ACTIONS);                      \
> -         (LEFT) > 0 && ofputil_action_is_valid(ITER, LEFT);             \
> -         ((LEFT) -= ntohs((ITER)->header.len) / sizeof(union ofp_action), \
> -          (ITER) = ofputil_action_next(ITER)))
> -
> -/* This macro does not check for actions with bad lengths.  It should only be
> - * used with actions from trusted sources or with actions that have already
> - * been validated (e.g. with OFPUTIL_ACTION_FOR_EACH).  */
> -#define OFPUTIL_ACTION_FOR_EACH_UNSAFE(ITER, LEFT, ACTIONS, N_ACTIONS)  \
> -    for ((ITER) = (ACTIONS), (LEFT) = (N_ACTIONS);                      \
> -         (LEFT) > 0;                                                    \
> -         ((LEFT) -= ntohs((ITER)->header.len) / sizeof(union ofp_action), \
> -          (ITER) = ofputil_action_next(ITER)))
> -
>  enum ofperr validate_actions(const union ofp_action *, size_t n_actions,
>                               const struct flow *, int max_ports);
>  bool action_outputs_to_port(const union ofp_action *, ovs_be16 port);
> diff --git a/ofproto/connmgr.c b/ofproto/connmgr.c
> index 8cdaa1f..93e5a71 100644
> --- a/ofproto/connmgr.c
> +++ b/ofproto/connmgr.c
> @@ -25,6 +25,7 @@
>  #include "fail-open.h"
>  #include "in-band.h"
>  #include "odp-util.h"
> +#include "ofp-actions.h"
>  #include "ofp-util.h"
>  #include "ofpbuf.h"
>  #include "ofproto-provider.h"
> @@ -1573,15 +1574,17 @@ connmgr_flushed(struct connmgr *mgr)
>       * traffic until a controller has been defined and it tells us to do so. */
>      if (!connmgr_has_controllers(mgr)
>          && mgr->fail_mode == OFPROTO_FAIL_STANDALONE) {
> -        union ofp_action action;
> +        struct ofpbuf ofpacts;
>          struct cls_rule rule;
>
> -        memset(&action, 0, sizeof action);
> -        action.type = htons(OFPAT10_OUTPUT);
> -        action.output.len = htons(sizeof action);
> -        action.output.port = htons(OFPP_NORMAL);
> +        ofpbuf_init(&ofpacts, OFPACT_OUTPUT_SIZE + OFPACT_END_SIZE);
> +        ofpact_put_OUTPUT(&ofpacts)->port = OFPP_NORMAL;
> +        ofpact_put_END(&ofpacts);
> +
>          cls_rule_init_catchall(&rule, 0);
> -        ofproto_add_flow(mgr->ofproto, &rule, &action, 1);
> +        ofproto_add_flow(mgr->ofproto, &rule, ofpacts.data, ofpacts.size);
> +
> +        ofpbuf_uninit(&ofpacts);
>      }
>  }
>
> diff --git a/ofproto/fail-open.c b/ofproto/fail-open.c
> index 912dc4e..3baa7b4 100644
> --- a/ofproto/fail-open.c
> +++ b/ofproto/fail-open.c
> @@ -23,6 +23,7 @@
>  #include "flow.h"
>  #include "mac-learning.h"
>  #include "odp-util.h"
> +#include "ofp-actions.h"
>  #include "ofp-util.h"
>  #include "ofpbuf.h"
>  #include "ofproto.h"
> @@ -214,18 +215,19 @@ fail_open_flushed(struct fail_open *fo)
>      int disconn_secs = connmgr_failure_duration(fo->connmgr);
>      bool open = disconn_secs >= trigger_duration(fo);
>      if (open) {
> -        union ofp_action action;
> +        struct ofpbuf ofpacts;
>          struct cls_rule rule;
>
>          /* Set up a flow that matches every packet and directs them to
>           * OFPP_NORMAL. */
> -        memset(&action, 0, sizeof action);
> -        action.type = htons(OFPAT10_OUTPUT);
> -        action.output.len = htons(sizeof action);
> -        action.output.port = htons(OFPP_NORMAL);
> +        ofpbuf_init(&ofpacts, OFPACT_OUTPUT_SIZE + OFPACT_END_SIZE);
> +        ofpact_put_OUTPUT(&ofpacts)->port = OFPP_NORMAL;
> +        ofpact_put_END(&ofpacts);
>
>          cls_rule_init_catchall(&rule, FAIL_OPEN_PRIORITY);
> -        ofproto_add_flow(fo->ofproto, &rule, &action, 1);
> +        ofproto_add_flow(fo->ofproto, &rule, ofpacts.data, ofpacts.size);
> +
> +        ofpbuf_uninit(&ofpacts);
>      }
>  }
>
> diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
> index f51182a..8013598 100644
> --- a/ofproto/ofproto-dpif.c
> +++ b/ofproto/ofproto-dpif.c
> @@ -42,6 +42,7 @@
>  #include "odp-util.h"
>  #include "ofp-util.h"
>  #include "ofpbuf.h"
> +#include "ofp-actions.h"
>  #include "ofp-parse.h"
>  #include "ofp-print.h"
>  #include "ofproto-dpif-governor.h"
> @@ -280,12 +281,10 @@ static void action_xlate_ctx_init(struct action_xlate_ctx *,
>                                    struct ofproto_dpif *, const struct flow *,
>                                    ovs_be16 initial_tci, struct rule_dpif *,
>                                    uint8_t tcp_flags, const struct ofpbuf *);
> -static void xlate_actions(struct action_xlate_ctx *,
> -                          const union ofp_action *in, size_t n_in,
> +static void xlate_actions(struct action_xlate_ctx *, const struct ofpact *in,
>                            struct ofpbuf *odp_actions);
>  static void xlate_actions_for_side_effects(struct action_xlate_ctx *,
> -                                           const union ofp_action *in,
> -                                           size_t n_in);
> +                                           const struct ofpact *in);
>
>  static size_t put_userspace_action(const struct ofproto_dpif *,
>                                     struct ofpbuf *odp_actions,
> @@ -804,7 +803,7 @@ construct(struct ofproto *ofproto_)
>
>  static int
>  add_internal_flow(struct ofproto_dpif *ofproto, int id,
> -                  const struct ofpbuf *actions, struct rule_dpif **rulep)
> +                  const struct ofpbuf *ofpacts, struct rule_dpif **rulep)
>  {
>      struct ofputil_flow_mod fm;
>      int error;
> @@ -821,8 +820,8 @@ add_internal_flow(struct ofproto_dpif *ofproto, int id,
>      fm.buffer_id = 0;
>      fm.out_port = 0;
>      fm.flags = 0;
> -    fm.actions = actions->data;
> -    fm.n_actions = actions->size / sizeof(union ofp_action);
> +    fm.ofpacts = ofpacts->data;
> +    fm.ofpacts_len = ofpacts->size;
>
>      error = ofproto_flow_mod(&ofproto->up, &fm);
>      if (error) {
> @@ -840,26 +839,29 @@ add_internal_flow(struct ofproto_dpif *ofproto, int id,
>  static int
>  add_internal_flows(struct ofproto_dpif *ofproto)
>  {
> -    struct nx_action_controller *nac;
> -    uint64_t actions_stub[128 / 8];
> -    struct ofpbuf actions;
> +    struct ofpact_controller *controller;
> +    uint64_t ofpacts_stub[128 / 8];
> +    struct ofpbuf ofpacts;
>      int error;
>      int id;
>
> -    ofpbuf_use_stack(&actions, actions_stub, sizeof actions_stub);
> +    ofpbuf_use_stack(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
>      id = 1;
>
> -    nac = ofputil_put_NXAST_CONTROLLER(&actions);
> -    nac->max_len = htons(UINT16_MAX);
> -    nac->controller_id = htons(0);
> -    nac->reason = OFPR_NO_MATCH;
> -    error = add_internal_flow(ofproto, id++, &actions, &ofproto->miss_rule);
> +    controller = ofpact_put_CONTROLLER(&ofpacts);
> +    controller->max_len = UINT16_MAX;
> +    controller->controller_id = 0;
> +    controller->reason = OFPR_NO_MATCH;
> +    ofpact_put_END(&ofpacts);
> +
> +    error = add_internal_flow(ofproto, id++, &ofpacts, &ofproto->miss_rule);
>      if (error) {
>          return error;
>      }
>
> -    ofpbuf_clear(&actions);
> -    error = add_internal_flow(ofproto, id++, &actions,
> +    ofpbuf_clear(&ofpacts);
> +    ofpact_put_END(&ofpacts);
> +    error = add_internal_flow(ofproto, id++, &ofpacts,
>                                &ofproto->no_packet_in_rule);
>      return error;
>  }
> @@ -2864,8 +2866,7 @@ handle_flow_miss_without_facet(struct flow_miss *miss,
>          action_xlate_ctx_init(&ctx, ofproto, &miss->flow, miss->initial_tci,
>                                rule, 0, packet);
>          ctx.resubmit_stats = &stats;
> -        xlate_actions(&ctx, rule->up.actions, rule->up.n_actions,
> -                      &odp_actions);
> +        xlate_actions(&ctx, rule->up.ofpacts, &odp_actions);
>
>          if (odp_actions.size) {
>              struct dpif_execute *execute = &op->dpif_op.u.execute;
> @@ -3699,8 +3700,7 @@ facet_learn(struct facet *facet)
>                            facet->flow.vlan_tci,
>                            facet->rule, facet->tcp_flags, NULL);
>      ctx.may_learn = true;
> -    xlate_actions_for_side_effects(&ctx, facet->rule->up.actions,
> -                                   facet->rule->up.n_actions);
> +    xlate_actions_for_side_effects(&ctx, facet->rule->up.ofpacts);
>  }
>
>  static void
> @@ -3761,10 +3761,15 @@ facet_account(struct facet *facet)
>  static bool
>  facet_is_controller_flow(struct facet *facet)
>  {
> -    return (facet
> -            && facet->rule->up.n_actions == 1
> -            && action_outputs_to_port(&facet->rule->up.actions[0],
> -                                      htons(OFPP_CONTROLLER)));
> +    if (facet) {
> +        const struct ofpact *ofpact = facet->rule->up.ofpacts;
> +
> +        if (ofpact->type == OFPACT_CONTROLLER &&
> +            ofpact_next(ofpact)->type == OFPACT_END) {
> +            return true;
> +        }
> +    }
> +    return false;
>  }
>
>  /* Folds all of 'facet''s statistics into its rule.  Also updates the
> @@ -3939,8 +3944,7 @@ facet_check_consistency(struct facet *facet)
>
>          action_xlate_ctx_init(&ctx, ofproto, &facet->flow,
>                                subfacet->initial_tci, rule, 0, NULL);
> -        xlate_actions(&ctx, rule->up.actions, rule->up.n_actions,
> -                      &odp_actions);
> +        xlate_actions(&ctx, rule->up.ofpacts, &odp_actions);
>
>          if (subfacet->path == SF_NOT_INSTALLED) {
>              /* This only happens if the datapath reported an error when we
> @@ -4049,8 +4053,7 @@ facet_revalidate(struct facet *facet)
>
>          action_xlate_ctx_init(&ctx, ofproto, &facet->flow,
>                                subfacet->initial_tci, new_rule, 0, NULL);
> -        xlate_actions(&ctx, new_rule->up.actions, new_rule->up.n_actions,
> -                      &odp_actions);
> +        xlate_actions(&ctx, new_rule->up.ofpacts, &odp_actions);
>
>          slow = (subfacet->slow & SLOW_MATCH) | ctx.slow;
>          if (subfacet_should_install(subfacet, slow, &odp_actions)) {
> @@ -4179,7 +4182,7 @@ flow_push_stats(struct rule_dpif *rule,
>      action_xlate_ctx_init(&ctx, ofproto, flow, flow->vlan_tci, rule,
>                            0, NULL);
>      ctx.resubmit_stats = stats;
> -    xlate_actions_for_side_effects(&ctx, rule->up.actions, rule->up.n_actions);
> +    xlate_actions_for_side_effects(&ctx, rule->up.ofpacts);
>  }
>
>  /* Subfacets. */
> @@ -4339,7 +4342,7 @@ subfacet_make_actions(struct subfacet *subfacet, const struct ofpbuf *packet,
>
>      action_xlate_ctx_init(&ctx, ofproto, &facet->flow, subfacet->initial_tci,
>                            rule, 0, packet);
> -    xlate_actions(&ctx, rule->up.actions, rule->up.n_actions, odp_actions);
> +    xlate_actions(&ctx, rule->up.ofpacts, odp_actions);
>      facet->tags = ctx.tags;
>      facet->has_learn = ctx.has_learn;
>      facet->has_normal = ctx.has_normal;
> @@ -4576,8 +4579,8 @@ rule_construct(struct rule *rule_)
>      uint8_t table_id;
>      enum ofperr error;
>
> -    error = validate_actions(rule->up.actions, rule->up.n_actions,
> -                             &rule->up.cr.flow, ofproto->max_ports);
> +    error = ofpacts_check(rule->up.ofpacts,
> +                          &rule->up.cr.flow, ofproto->max_ports);
>      if (error) {
>          return error;
>      }
> @@ -4669,7 +4672,7 @@ rule_execute(struct rule *rule_, const struct flow *flow,
>      action_xlate_ctx_init(&ctx, ofproto, flow, flow->vlan_tci,
>                            rule, stats.tcp_flags, packet);
>      ctx.resubmit_stats = &stats;
> -    xlate_actions(&ctx, rule->up.actions, rule->up.n_actions, &odp_actions);
> +    xlate_actions(&ctx, rule->up.ofpacts, &odp_actions);
>
>      execute_odp_actions(ofproto, flow, odp_actions.data,
>                          odp_actions.size, packet);
> @@ -4686,8 +4689,8 @@ rule_modify_actions(struct rule *rule_)
>      struct ofproto_dpif *ofproto = ofproto_dpif_cast(rule->up.ofproto);
>      enum ofperr error;
>
> -    error = validate_actions(rule->up.actions, rule->up.n_actions,
> -                             &rule->up.cr.flow, ofproto->max_ports);
> +    error = ofpacts_check(rule->up.ofpacts, &rule->up.cr.flow,
> +                          ofproto->max_ports);
>      if (error) {
>          ofoperation_complete(rule->up.pending, error);
>          return;
> @@ -4740,8 +4743,7 @@ send_packet(const struct ofport_dpif *ofport, struct ofpbuf *packet)
>
>  /* OpenFlow to datapath action translation. */
>
> -static void do_xlate_actions(const union ofp_action *in, size_t n_in,
> -                             struct action_xlate_ctx *ctx);
> +static void do_xlate_actions(const struct ofpact *, struct action_xlate_ctx *);
>  static void xlate_normal(struct action_xlate_ctx *);
>
>  /* Composes an ODP action for a "slow path" action for 'flow' within 'ofproto'.
> @@ -4987,7 +4989,7 @@ xlate_table_action(struct action_xlate_ctx *ctx,
>
>              ctx->recurse++;
>              ctx->rule = rule;
> -            do_xlate_actions(rule->up.actions, rule->up.n_actions, ctx);
> +            do_xlate_actions(rule->up.ofpacts, ctx);
>              ctx->rule = old_rule;
>              ctx->recurse--;
>          }
> @@ -5003,16 +5005,21 @@ xlate_table_action(struct action_xlate_ctx *ctx,
>  }
>
>  static void
> -xlate_resubmit_table(struct action_xlate_ctx *ctx,
> -                     const struct nx_action_resubmit *nar)
> +xlate_ofpact_resubmit(struct action_xlate_ctx *ctx,
> +                      const struct ofpact_resubmit *resubmit)
>  {
>      uint16_t in_port;
>      uint8_t table_id;
>
> -    in_port = (nar->in_port == htons(OFPP_IN_PORT)
> -               ? ctx->flow.in_port
> -               : ntohs(nar->in_port));
> -    table_id = nar->table == 255 ? ctx->table_id : nar->table;
> +    in_port = resubmit->in_port;
> +    if (in_port == OFPP_IN_PORT) {
> +        in_port = ctx->flow.in_port;
> +    }
> +
> +    table_id = resubmit->table_id;
> +    if (table_id == 255) {
> +        table_id = ctx->table_id;
> +    }
>
>      xlate_table_action(ctx, in_port, table_id);
>  }
> @@ -5125,8 +5132,8 @@ compose_dec_ttl(struct action_xlate_ctx *ctx)
>  }
>
>  static void
> -xlate_output_action__(struct action_xlate_ctx *ctx,
> -                      uint16_t port, uint16_t max_len)
> +xlate_output_action(struct action_xlate_ctx *ctx,
> +                    uint16_t port, uint16_t max_len)
>  {
>      uint16_t prev_nf_output_iface = ctx->nf_output_iface;
>
> @@ -5173,44 +5180,32 @@ xlate_output_action__(struct action_xlate_ctx *ctx,
>
>  static void
>  xlate_output_reg_action(struct action_xlate_ctx *ctx,
> -                        const struct nx_action_output_reg *naor)
> +                        const struct ofpact_output_reg *or)
>  {
> -    struct mf_subfield src;
> -    uint64_t ofp_port;
> -
> -    nxm_decode(&src, naor->src, naor->ofs_nbits);
> -    ofp_port = mf_get_subfield(&src, &ctx->flow);
> -
> -    if (ofp_port <= UINT16_MAX) {
> -        xlate_output_action__(ctx, ofp_port, ntohs(naor->max_len));
> +    uint64_t port = mf_get_subfield(&or->src, &ctx->flow);
> +    if (port <= UINT16_MAX) {
> +        xlate_output_action(ctx, port, or->max_len);
>      }
>  }
>
>  static void
> -xlate_output_action(struct action_xlate_ctx *ctx,
> -                    const struct ofp_action_output *oao)
> -{
> -    xlate_output_action__(ctx, ntohs(oao->port), ntohs(oao->max_len));
> -}
> -
> -static void
>  xlate_enqueue_action(struct action_xlate_ctx *ctx,
> -                     const struct ofp_action_enqueue *oae)
> +                     const struct ofpact_enqueue *enqueue)
>  {
> -    uint16_t ofp_port;
> +    uint16_t ofp_port = enqueue->port;
> +    uint32_t queue_id = enqueue->queue;
>      uint32_t flow_priority, priority;
>      int error;
>
> -    error = dpif_queue_to_priority(ctx->ofproto->dpif, ntohl(oae->queue_id),
> -                                   &priority);
> +    /* Translate queue to priority. */
> +    error = dpif_queue_to_priority(ctx->ofproto->dpif, queue_id, &priority);
>      if (error) {
>          /* Fall back to ordinary output action. */
> -        xlate_output_action__(ctx, ntohs(oae->port), 0);
> +        xlate_output_action(ctx, enqueue->port, 0);
>          return;
>      }
>
> -    /* Figure out datapath output port. */
> -    ofp_port = ntohs(oae->port);
> +    /* Check output port. */
>      if (ofp_port == OFPP_IN_PORT) {
>          ofp_port = ctx->flow.in_port;
>      } else if (ofp_port == ctx->flow.in_port) {
> @@ -5232,21 +5227,16 @@ xlate_enqueue_action(struct action_xlate_ctx *ctx,
>  }
>
>  static void
> -xlate_set_queue_action(struct action_xlate_ctx *ctx,
> -                       const struct nx_action_set_queue *nasq)
> +xlate_set_queue_action(struct action_xlate_ctx *ctx, uint32_t queue_id)
>  {
> -    uint32_t priority;
> -    int error;
> +    uint32_t skb_priority;
>
> -    error = dpif_queue_to_priority(ctx->ofproto->dpif, ntohl(nasq->queue_id),
> -                                   &priority);
> -    if (error) {
> -        /* Couldn't translate queue to a priority, so ignore.  A warning
> +    if (!dpif_queue_to_priority(ctx->ofproto->dpif, queue_id, &skb_priority)) {
> +        ctx->flow.skb_priority = skb_priority;
> +    } else {
> +        /* Couldn't translate queue to a priority.  Nothing to do.  A warning
>           * has already been logged. */
> -        return;
>      }
> -
> -    ctx->flow.skb_priority = priority;
>  }
>
>  struct xlate_reg_state {
> @@ -5256,9 +5246,9 @@ struct xlate_reg_state {
>
>  static void
>  xlate_autopath(struct action_xlate_ctx *ctx,
> -               const struct nx_action_autopath *naa)
> +               const struct ofpact_autopath *ap)
>  {
> -    uint16_t ofp_port = ntohl(naa->id);
> +    uint16_t ofp_port = ap->port;
>      struct ofport_dpif *port = get_ofp_port(ctx->ofproto, ofp_port);
>
>      if (!port || !port->bundle) {
> @@ -5271,7 +5261,7 @@ xlate_autopath(struct action_xlate_ctx *ctx,
>              ofp_port = slave->up.ofp_port;
>          }
>      }
> -    autopath_execute(naa, &ctx->flow, ofp_port);
> +    nxm_reg_load(&ap->dst, ofp_port, &ctx->flow);
>  }
>
>  static bool
> @@ -5297,14 +5287,31 @@ slave_enabled_cb(uint16_t ofp_port, void *ofproto_)
>  }
>
>  static void
> +xlate_bundle_action(struct action_xlate_ctx *ctx,
> +                    const struct ofpact_bundle *bundle)
> +{
> +    uint16_t port;
> +
> +    port = bundle_execute(bundle, &ctx->flow, slave_enabled_cb, ctx->ofproto);
> +    if (bundle->dst.field) {
> +        nxm_reg_load(&bundle->dst, port, &ctx->flow);
> +    } else {
> +        xlate_output_action(ctx, port, 0);
> +    }
> +}
> +
> +static void
>  xlate_learn_action(struct action_xlate_ctx *ctx,
> -                   const struct nx_action_learn *learn)
> +                   const struct ofpact_learn *learn)
>  {
>      static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
>      struct ofputil_flow_mod fm;
> +    uint64_t ofpacts_stub[1024 / 8];
> +    struct ofpbuf ofpacts;
>      int error;
>
> -    learn_execute(learn, &ctx->flow, &fm);
> +    ofpbuf_use_stack(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
> +    learn_execute(learn, &ctx->flow, &fm, &ofpacts);
>
>      error = ofproto_flow_mod(&ctx->ofproto->up, &fm);
>      if (error && !VLOG_DROP_WARN(&rl)) {
> @@ -5312,7 +5319,7 @@ xlate_learn_action(struct action_xlate_ctx *ctx,
>                    ofperr_get_name(error));
>      }
>
> -    free(fm.actions);
> +    ofpbuf_uninit(&ofpacts);
>  }
>
>  /* Reduces '*timeout' to no more than 'max'.  A value of zero in either case
> @@ -5327,13 +5334,13 @@ reduce_timeout(uint16_t max, uint16_t *timeout)
>
>  static void
>  xlate_fin_timeout(struct action_xlate_ctx *ctx,
> -                  const struct nx_action_fin_timeout *naft)
> +                  const struct ofpact_fin_timeout *oft)
>  {
>      if (ctx->tcp_flags & (TCP_FIN | TCP_RST) && ctx->rule) {
>          struct rule_dpif *rule = ctx->rule;
>
> -        reduce_timeout(ntohs(naft->fin_idle_timeout), &rule->up.idle_timeout);
> -        reduce_timeout(ntohs(naft->fin_hard_timeout), &rule->up.hard_timeout);
> +        reduce_timeout(oft->fin_idle_timeout, &rule->up.idle_timeout);
> +        reduce_timeout(oft->fin_hard_timeout, &rule->up.hard_timeout);
>      }
>  }
>
> @@ -5359,13 +5366,11 @@ may_receive(const struct ofport_dpif *port, struct action_xlate_ctx *ctx)
>  }
>
>  static void
> -do_xlate_actions(const union ofp_action *in, size_t n_in,
> -                 struct action_xlate_ctx *ctx)
> +do_xlate_actions(const struct ofpact *ofpacts, struct action_xlate_ctx *ctx)
>  {
>      const struct ofport_dpif *port;
> -    const union ofp_action *ia;
>      bool was_evictable = true;
> -    size_t left;
> +    const struct ofpact *a;
>
>      port = get_ofp_port(ctx->ofproto, ctx->flow.in_port);
>      if (port && !may_receive(port, ctx)) {
> @@ -5378,184 +5383,149 @@ do_xlate_actions(const union ofp_action *in, size_t n_in,
>          was_evictable = ctx->rule->up.evictable;
>          ctx->rule->up.evictable = false;
>      }
> -    OFPUTIL_ACTION_FOR_EACH_UNSAFE (ia, left, in, n_in) {
> -        const struct ofp_action_dl_addr *oada;
> -        const struct nx_action_resubmit *nar;
> -        const struct nx_action_set_tunnel *nast;
> -        const struct nx_action_set_queue *nasq;
> -        const struct nx_action_multipath *nam;
> -        const struct nx_action_autopath *naa;
> -        const struct nx_action_bundle *nab;
> -        const struct nx_action_output_reg *naor;
> -        const struct nx_action_controller *nac;
> -        enum ofputil_action_code code;
> -        ovs_be64 tun_id;
> +    OFPACT_FOR_EACH (a, ofpacts) {
> +        struct ofpact_controller *controller;
>
>          if (ctx->exit) {
>              break;
>          }
>
> -        code = ofputil_decode_action_unsafe(ia);
> -        switch (code) {
> -        case OFPUTIL_ACTION_INVALID:
> -            NOT_REACHED();
> +        switch (a->type) {
> +        case OFPACT_END:
> +            goto out;
> +
> +        case OFPACT_OUTPUT:
> +            xlate_output_action(ctx, ofpact_get_OUTPUT(a)->port,
> +                                ofpact_get_OUTPUT(a)->max_len);
> +            break;
> +
> +        case OFPACT_CONTROLLER:
> +            controller = ofpact_get_CONTROLLER(a);
> +            execute_controller_action(ctx, controller->max_len,
> +                                      controller->reason,
> +                                      controller->controller_id);
> +            break;
>
> -        case OFPUTIL_OFPAT10_OUTPUT:
> -            xlate_output_action(ctx, &ia->output);
> +        case OFPACT_ENQUEUE:
> +            xlate_enqueue_action(ctx, ofpact_get_ENQUEUE(a));
>              break;
>
> -        case OFPUTIL_OFPAT10_SET_VLAN_VID:
> +        case OFPACT_SET_VLAN_VID:
>              ctx->flow.vlan_tci &= ~htons(VLAN_VID_MASK);
> -            ctx->flow.vlan_tci |= ia->vlan_vid.vlan_vid | htons(VLAN_CFI);
> +            ctx->flow.vlan_tci |= (htons(ofpact_get_SET_VLAN_VID(a)->vlan_vid)
> +                                   | htons(VLAN_CFI));
>              break;
>
> -        case OFPUTIL_OFPAT10_SET_VLAN_PCP:
> +        case OFPACT_SET_VLAN_PCP:
>              ctx->flow.vlan_tci &= ~htons(VLAN_PCP_MASK);
> -            ctx->flow.vlan_tci |= htons(
> -                (ia->vlan_pcp.vlan_pcp << VLAN_PCP_SHIFT) | VLAN_CFI);
> +            ctx->flow.vlan_tci |= htons((ofpact_get_SET_VLAN_PCP(a)->vlan_pcp
> +                                         << VLAN_PCP_SHIFT)
> +                                        | VLAN_CFI);
>              break;
>
> -        case OFPUTIL_OFPAT10_STRIP_VLAN:
> +        case OFPACT_STRIP_VLAN:
>              ctx->flow.vlan_tci = htons(0);
>              break;
>
> -        case OFPUTIL_OFPAT10_SET_DL_SRC:
> -            oada = ((struct ofp_action_dl_addr *) ia);
> -            memcpy(ctx->flow.dl_src, oada->dl_addr, ETH_ADDR_LEN);
> +        case OFPACT_SET_ETH_SRC:
> +            memcpy(ctx->flow.dl_src, ofpact_get_SET_ETH_SRC(a)->mac,
> +                   ETH_ADDR_LEN);
>              break;
>
> -        case OFPUTIL_OFPAT10_SET_DL_DST:
> -            oada = ((struct ofp_action_dl_addr *) ia);
> -            memcpy(ctx->flow.dl_dst, oada->dl_addr, ETH_ADDR_LEN);
> +        case OFPACT_SET_ETH_DST:
> +            memcpy(ctx->flow.dl_dst, ofpact_get_SET_ETH_DST(a)->mac,
> +                   ETH_ADDR_LEN);
>              break;
>
> -        case OFPUTIL_OFPAT10_SET_NW_SRC:
> -            ctx->flow.nw_src = ia->nw_addr.nw_addr;
> +        case OFPACT_SET_IPV4_SRC:
> +            ctx->flow.nw_src = ofpact_get_SET_IPV4_SRC(a)->ipv4;
>              break;
>
> -        case OFPUTIL_OFPAT10_SET_NW_DST:
> -            ctx->flow.nw_dst = ia->nw_addr.nw_addr;
> +        case OFPACT_SET_IPV4_DST:
> +            ctx->flow.nw_dst = ofpact_get_SET_IPV4_DST(a)->ipv4;
>              break;
>
> -        case OFPUTIL_OFPAT10_SET_NW_TOS:
> +        case OFPACT_SET_IPV4_DSCP:
>              /* OpenFlow 1.0 only supports IPv4. */
>              if (ctx->flow.dl_type == htons(ETH_TYPE_IP)) {
>                  ctx->flow.nw_tos &= ~IP_DSCP_MASK;
> -                ctx->flow.nw_tos |= ia->nw_tos.nw_tos & IP_DSCP_MASK;
> +                ctx->flow.nw_tos |= ofpact_get_SET_IPV4_DSCP(a)->dscp;
>              }
>              break;
>
> -        case OFPUTIL_OFPAT10_SET_TP_SRC:
> -            ctx->flow.tp_src = ia->tp_port.tp_port;
> -            break;
> -
> -        case OFPUTIL_OFPAT10_SET_TP_DST:
> -            ctx->flow.tp_dst = ia->tp_port.tp_port;
> +        case OFPACT_SET_L4_SRC_PORT:
> +            ctx->flow.tp_src = htons(ofpact_get_SET_L4_SRC_PORT(a)->port);
>              break;
>
> -        case OFPUTIL_OFPAT10_ENQUEUE:
> -            xlate_enqueue_action(ctx, (const struct ofp_action_enqueue *) ia);
> +        case OFPACT_SET_L4_DST_PORT:
> +            ctx->flow.tp_dst = htons(ofpact_get_SET_L4_DST_PORT(a)->port);
>              break;
>
> -        case OFPUTIL_NXAST_RESUBMIT:
> -            nar = (const struct nx_action_resubmit *) ia;
> -            xlate_table_action(ctx, ntohs(nar->in_port), ctx->table_id);
> +        case OFPACT_RESUBMIT:
> +            xlate_ofpact_resubmit(ctx, ofpact_get_RESUBMIT(a));
>              break;
>
> -        case OFPUTIL_NXAST_RESUBMIT_TABLE:
> -            xlate_resubmit_table(ctx, (const struct nx_action_resubmit *) ia);
> +        case OFPACT_SET_TUNNEL:
> +            ctx->flow.tun_id = htonll(ofpact_get_SET_TUNNEL(a)->tun_id);
>              break;
>
> -        case OFPUTIL_NXAST_SET_TUNNEL:
> -            nast = (const struct nx_action_set_tunnel *) ia;
> -            tun_id = htonll(ntohl(nast->tun_id));
> -            ctx->flow.tun_id = tun_id;
> +        case OFPACT_SET_QUEUE:
> +            xlate_set_queue_action(ctx, ofpact_get_SET_QUEUE(a)->queue_id);
>              break;
>
> -        case OFPUTIL_NXAST_SET_QUEUE:
> -            nasq = (const struct nx_action_set_queue *) ia;
> -            xlate_set_queue_action(ctx, nasq);
> -            break;
> -
> -        case OFPUTIL_NXAST_POP_QUEUE:
> +        case OFPACT_POP_QUEUE:
>              ctx->flow.skb_priority = ctx->orig_skb_priority;
>              break;
>
> -        case OFPUTIL_NXAST_REG_MOVE:
> -            nxm_execute_reg_move((const struct nx_action_reg_move *) ia,
> -                                 &ctx->flow);
> +        case OFPACT_REG_MOVE:
> +            nxm_execute_reg_move(ofpact_get_REG_MOVE(a), &ctx->flow);
>              break;
>
> -        case OFPUTIL_NXAST_REG_LOAD:
> -            nxm_execute_reg_load((const struct nx_action_reg_load *) ia,
> -                                 &ctx->flow);
> +        case OFPACT_REG_LOAD:
> +            nxm_execute_reg_load(ofpact_get_REG_LOAD(a), &ctx->flow);
>              break;
>
> -        case OFPUTIL_NXAST_NOTE:
> -            /* Nothing to do. */
> -            break;
> -
> -        case OFPUTIL_NXAST_SET_TUNNEL64:
> -            tun_id = ((const struct nx_action_set_tunnel64 *) ia)->tun_id;
> -            ctx->flow.tun_id = tun_id;
> +        case OFPACT_DEC_TTL:
> +            if (compose_dec_ttl(ctx)) {
> +                goto out;
> +            }
>              break;
>
> -        case OFPUTIL_NXAST_MULTIPATH:
> -            nam = (const struct nx_action_multipath *) ia;
> -            multipath_execute(nam, &ctx->flow);
> +        case OFPACT_NOTE:
> +            /* Nothing to do. */
>              break;
>
> -        case OFPUTIL_NXAST_AUTOPATH:
> -            naa = (const struct nx_action_autopath *) ia;
> -            xlate_autopath(ctx, naa);
> +        case OFPACT_MULTIPATH:
> +            multipath_execute(ofpact_get_MULTIPATH(a), &ctx->flow);
>              break;
>
> -        case OFPUTIL_NXAST_BUNDLE:
> -            ctx->ofproto->has_bundle_action = true;
> -            nab = (const struct nx_action_bundle *) ia;
> -            xlate_output_action__(ctx, bundle_execute(nab, &ctx->flow,
> -                                                      slave_enabled_cb,
> -                                                      ctx->ofproto), 0);
> +        case OFPACT_AUTOPATH:
> +            xlate_autopath(ctx, ofpact_get_AUTOPATH(a));
>              break;
>
> -        case OFPUTIL_NXAST_BUNDLE_LOAD:
> +        case OFPACT_BUNDLE:
>              ctx->ofproto->has_bundle_action = true;
> -            nab = (const struct nx_action_bundle *) ia;
> -            bundle_execute_load(nab, &ctx->flow, slave_enabled_cb,
> -                                ctx->ofproto);
> +            xlate_bundle_action(ctx, ofpact_get_BUNDLE(a));
>              break;
>
> -        case OFPUTIL_NXAST_OUTPUT_REG:
> -            naor = (const struct nx_action_output_reg *) ia;
> -            xlate_output_reg_action(ctx, naor);
> +        case OFPACT_OUTPUT_REG:
> +            xlate_output_reg_action(ctx, ofpact_get_OUTPUT_REG(a));
>              break;
>
> -        case OFPUTIL_NXAST_LEARN:
> +        case OFPACT_LEARN:
>              ctx->has_learn = true;
>              if (ctx->may_learn) {
> -                xlate_learn_action(ctx, (const struct nx_action_learn *) ia);
> +                xlate_learn_action(ctx, ofpact_get_LEARN(a));
>              }
>              break;
>
> -        case OFPUTIL_NXAST_DEC_TTL:
> -            if (compose_dec_ttl(ctx)) {
> -                goto out;
> -            }
> -            break;
> -
> -        case OFPUTIL_NXAST_EXIT:
> +        case OFPACT_EXIT:
>              ctx->exit = true;
>              break;
>
> -        case OFPUTIL_NXAST_FIN_TIMEOUT:
> +        case OFPACT_FIN_TIMEOUT:
>              ctx->has_fin_timeout = true;
> -            xlate_fin_timeout(ctx, (const struct nx_action_fin_timeout *) ia);
> -            break;
> -
> -        case OFPUTIL_NXAST_CONTROLLER:
> -            nac = (const struct nx_action_controller *) ia;
> -            execute_controller_action(ctx, ntohs(nac->max_len), nac->reason,
> -                                      ntohs(nac->controller_id));
> +            xlate_fin_timeout(ctx, ofpact_get_FIN_TIMEOUT(a));
>              break;
>          }
>      }
> @@ -5594,8 +5564,7 @@ action_xlate_ctx_init(struct action_xlate_ctx *ctx,
>  /* Translates the 'n_in' "union ofp_action"s in 'in' into datapath actions in
>   * 'odp_actions', using 'ctx'. */
>  static void
> -xlate_actions(struct action_xlate_ctx *ctx,
> -              const union ofp_action *in, size_t n_in,
> +xlate_actions(struct action_xlate_ctx *ctx, const struct ofpact *ofpacts,
>                struct ofpbuf *odp_actions)
>  {
>      /* Normally false.  Set to true if we ever hit MAX_RESUBMIT_RECURSION, so
> @@ -5665,7 +5634,7 @@ xlate_actions(struct action_xlate_ctx *ctx,
>          ovs_be16 initial_tci = ctx->base_flow.vlan_tci;
>
>          add_sflow_action(ctx);
> -        do_xlate_actions(in, n_in, ctx);
> +        do_xlate_actions(ofpacts, ctx);
>
>          if (ctx->max_resubmit_trigger && !ctx->resubmit_hook) {
>              if (!hit_resubmit_limit) {
> @@ -5704,13 +5673,13 @@ xlate_actions(struct action_xlate_ctx *ctx,
>   * using 'ctx', and discards the datapath actions. */
>  static void
>  xlate_actions_for_side_effects(struct action_xlate_ctx *ctx,
> -                               const union ofp_action *in, size_t n_in)
> +                               const struct ofpact *in)
>  {
>      uint64_t odp_actions_stub[1024 / 8];
>      struct ofpbuf odp_actions;
>
>      ofpbuf_use_stub(&odp_actions, odp_actions_stub, sizeof odp_actions_stub);
> -    xlate_actions(ctx, in, n_in, &odp_actions);
> +    xlate_actions(ctx, in, &odp_actions);
>      ofpbuf_uninit(&odp_actions);
>  }
>
> @@ -6370,8 +6339,7 @@ set_frag_handling(struct ofproto *ofproto_,
>
>  static enum ofperr
>  packet_out(struct ofproto *ofproto_, struct ofpbuf *packet,
> -           const struct flow *flow,
> -           const union ofp_action *ofp_actions, size_t n_ofp_actions)
> +           const struct flow *flow, const struct ofpact *ofpacts)
>  {
>      struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
>      enum ofperr error;
> @@ -6380,8 +6348,7 @@ packet_out(struct ofproto *ofproto_, struct ofpbuf *packet,
>          return OFPERR_NXBRC_BAD_IN_PORT;
>      }
>
> -    error = validate_actions(ofp_actions, n_ofp_actions, flow,
> -                             ofproto->max_ports);
> +    error = ofpacts_check(ofpacts, flow, ofproto->max_ports);
>      if (!error) {
>          struct odputil_keybuf keybuf;
>          struct dpif_flow_stats stats;
> @@ -6403,7 +6370,7 @@ packet_out(struct ofproto *ofproto_, struct ofpbuf *packet,
>
>          ofpbuf_use_stub(&odp_actions,
>                          odp_actions_stub, sizeof odp_actions_stub);
> -        xlate_actions(&ctx, ofp_actions, n_ofp_actions, &odp_actions);
> +        xlate_actions(&ctx, ofpacts, &odp_actions);
>          dpif_execute(ofproto->dpif, key.data, key.size,
>                       odp_actions.data, odp_actions.size, packet);
>          ofpbuf_uninit(&odp_actions);
> @@ -6560,7 +6527,7 @@ trace_format_rule(struct ds *result, uint8_t table_id, int level,
>
>      ds_put_char_multiple(result, '\t', level);
>      ds_put_cstr(result, "OpenFlow ");
> -    ofp_print_actions(result, rule->up.actions, rule->up.n_actions);
> +    ofpacts_format(rule->up.ofpacts, result);
>      ds_put_char(result, '\n');
>  }
>
> @@ -6768,8 +6735,7 @@ ofproto_trace(struct ofproto_dpif *ofproto, const struct flow *flow,
>          action_xlate_ctx_init(&trace.ctx, ofproto, flow, initial_tci,
>                                rule, tcp_flags, packet);
>          trace.ctx.resubmit_hook = trace_resubmit;
> -        xlate_actions(&trace.ctx, rule->up.actions, rule->up.n_actions,
> -                      &odp_actions);
> +        xlate_actions(&trace.ctx, rule->up.ofpacts, &odp_actions);
>
>          ds_put_char(ds, '\n');
>          trace_format_flow(ds, 0, "Final flow", &trace);
> diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h
> index 2cbb9ae..ae9ace5 100644
> --- a/ofproto/ofproto-provider.h
> +++ b/ofproto/ofproto-provider.h
> @@ -29,6 +29,7 @@
>  #include "shash.h"
>  #include "timeval.h"
>
> +struct ofpact;
>  struct ofputil_flow_mod;
>  struct simap;
>
> @@ -184,8 +185,8 @@ struct rule {
>      struct heap_node evg_node;   /* In eviction_group's "rules" heap. */
>      struct eviction_group *eviction_group; /* NULL if not in any group. */
>
> -    union ofp_action *actions;   /* OpenFlow actions. */
> -    int n_actions;               /* Number of elements in actions[]. */
> +    struct ofpact *ofpacts;      /* "OFPACT_*"s.  Ends with OFPACT_END. */
> +    unsigned int ofpacts_len;    /* Size of 'ofpacts', in bytes. */
>  };
>
>  static inline struct rule *
> @@ -781,13 +782,11 @@ struct ofproto_class {
>       *     registers, then it is an error if 'rule->cr' does not wildcard all
>       *     registers.
>       *
> -     *   - Validate that 'rule->actions' and 'rule->n_actions' are well-formed
> -     *     OpenFlow actions that the datapath can correctly implement.  The
> -     *     validate_actions() function (in ofp-util.c) can be useful as a model
> -     *     for action validation, but it accepts all of the OpenFlow actions
> -     *     that OVS understands.  If your ofproto implementation only
> -     *     implements a subset of those, then you should implement your own
> -     *     action validation.
> +     *   - Validate that 'rule->ofpacts' is a sequence of well-formed actions
> +     *     that the datapath can correctly implement.  If your ofproto
> +     *     implementation only implements a subset of the actions that Open
> +     *     vSwitch understands, then you should implement your own action
> +     *     validation.
>       *
>       *   - If the rule is valid, update the datapath flow table, adding the new
>       *     rule or replacing the existing one.
> @@ -918,14 +917,13 @@ struct ofproto_class {
>                                enum ofp_config_flags frag_handling);
>
>      /* Implements the OpenFlow OFPT_PACKET_OUT command.  The datapath should
> -     * execute the 'n_actions' in the 'actions' array on 'packet'.
> +     * execute the sequence of 'ofpacts' (which ends with OFPACT_END).
>       *
> -     * The caller retains ownership of 'packet', so ->packet_out() should not
> -     * modify or free it.
> +     * The caller retains ownership of 'packet' and of 'ofpacts', so
> +     * ->packet_out() should not modify or free them.
>       *
> -     * This function must validate that the 'n_actions' elements in 'actions'
> -     * are well-formed OpenFlow actions that can be correctly implemented by
> -     * the datapath.  If not, then it should return an OpenFlow error code.
> +     * This function must validate that it can implement 'ofpacts'.  If not,
> +     * then it should return an OpenFlow error code.
>       *
>       * 'flow' reflects the flow information for 'packet'.  All of the
>       * information in 'flow' is extracted from 'packet', except for
> @@ -957,8 +955,7 @@ struct ofproto_class {
>       * Returns 0 if successful, otherwise an OpenFlow error code. */
>      enum ofperr (*packet_out)(struct ofproto *ofproto, struct ofpbuf *packet,
>                                const struct flow *flow,
> -                              const union ofp_action *actions,
> -                              size_t n_actions);
> +                              const struct ofpact *ofpacts);
>
>  /* ## ------------------------- ## */
>  /* ## OFPP_NORMAL configuration ## */
> @@ -1195,7 +1192,7 @@ BUILD_ASSERT_DECL(OFPROTO_POSTPONE < OFPERR_OFS);
>
>  int ofproto_flow_mod(struct ofproto *, const struct ofputil_flow_mod *);
>  void ofproto_add_flow(struct ofproto *, const struct cls_rule *,
> -                      const union ofp_action *, size_t n_actions);
> +                      const struct ofpact *ofpacts, size_t ofpacts_len);
>  bool ofproto_delete_flow(struct ofproto *, const struct cls_rule *);
>  void ofproto_flush_flows(struct ofproto *);
>
> diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
> index 1a2f712..0d3fa1b 100644
> --- a/ofproto/ofproto.c
> +++ b/ofproto/ofproto.c
> @@ -32,6 +32,7 @@
>  #include "meta-flow.h"
>  #include "netdev.h"
>  #include "nx-match.h"
> +#include "ofp-actions.h"
>  #include "ofp-errors.h"
>  #include "ofp-print.h"
>  #include "ofp-util.h"
> @@ -118,8 +119,8 @@ struct ofoperation {
>      struct rule *rule;          /* Rule being operated upon. */
>      enum ofoperation_type type; /* Type of operation. */
>      struct rule *victim;        /* OFOPERATION_ADDING: Replaced rule. */
> -    union ofp_action *actions;  /* OFOPERATION_MODIFYING: Replaced actions. */
> -    int n_actions;              /* OFOPERATION_MODIFYING: # of old actions. */
> +    struct ofpact *ofpacts;     /* OFOPERATION_MODIFYING: Replaced actions. */
> +    size_t ofpacts_len;         /* OFOPERATION_MODIFYING: Bytes of ofpacts. */
>      ovs_be64 flow_cookie;       /* Rule's old flow cookie. */
>  };
>
> @@ -1378,27 +1379,28 @@ ofproto_port_del(struct ofproto *ofproto, uint16_t ofp_port)
>   * (0...65535, inclusive) then the flow will be visible to OpenFlow
>   * controllers; otherwise, it will be hidden.
>   *
> - * The caller retains ownership of 'cls_rule' and 'actions'.
> + * The caller retains ownership of 'cls_rule' and 'ofpacts'.
>   *
>   * This is a helper function for in-band control and fail-open. */
>  void
>  ofproto_add_flow(struct ofproto *ofproto, const struct cls_rule *cls_rule,
> -                 const union ofp_action *actions, size_t n_actions)
> +                 const struct ofpact *ofpacts, size_t ofpacts_len)
>  {
>      const struct rule *rule;
>
>      rule = rule_from_cls_rule(classifier_find_rule_exactly(
>                                      &ofproto->tables[0].cls, cls_rule));
> -    if (!rule || !ofputil_actions_equal(rule->actions, rule->n_actions,
> -                                        actions, n_actions)) {
> +    if (!rule || !ofpacts_equal(rule->ofpacts, rule->ofpacts_len,
> +                                ofpacts, ofpacts_len)) {
>          struct ofputil_flow_mod fm;
>
>          memset(&fm, 0, sizeof fm);
>          fm.cr = *cls_rule;
>          fm.buffer_id = UINT32_MAX;
> -        fm.actions = (union ofp_action *) actions;
> -        fm.n_actions = n_actions;
> +        fm.ofpacts = xmemdup(ofpacts, ofpacts_len);
> +        fm.ofpacts_len = ofpacts_len;
>          add_flow(ofproto, NULL, &fm, NULL);
> +        free(fm.ofpacts);
>      }
>  }
>
> @@ -1857,7 +1859,7 @@ static void
>  ofproto_rule_destroy__(struct rule *rule)
>  {
>      if (rule) {
> -        free(rule->actions);
> +        free(rule->ofpacts);
>          rule->ofproto->ofproto_class->rule_dealloc(rule);
>      }
>  }
> @@ -1879,23 +1881,11 @@ ofproto_rule_destroy(struct rule *rule)
>  }
>
>  /* Returns true if 'rule' has an OpenFlow OFPAT_OUTPUT or OFPAT_ENQUEUE action
> - * that outputs to 'out_port' (output to OFPP_FLOOD and OFPP_ALL doesn't
> - * count). */
> + * that outputs to 'port' (output to OFPP_FLOOD and OFPP_ALL doesn't count). */
>  static bool
> -rule_has_out_port(const struct rule *rule, uint16_t out_port)
> +rule_has_out_port(const struct rule *rule, uint16_t port)
>  {
> -    const union ofp_action *oa;
> -    size_t left;
> -
> -    if (out_port == OFPP_NONE) {
> -        return true;
> -    }
> -    OFPUTIL_ACTION_FOR_EACH_UNSAFE (oa, left, rule->actions, rule->n_actions) {
> -        if (action_outputs_to_port(oa, htons(out_port))) {
> -            return true;
> -        }
> -    }
> -    return false;
> +    return port == OFPP_NONE || ofpacts_output_to_port(rule->ofpacts, port);
>  }
>
>  /* Executes the actions indicated by 'rule' on 'packet' and credits 'rule''s
> @@ -2052,6 +2042,8 @@ handle_packet_out(struct ofconn *ofconn, const struct ofp_packet_out *opo)
>      struct ofproto *p = ofconn_get_ofproto(ofconn);
>      struct ofputil_packet_out po;
>      struct ofpbuf *payload;
> +    uint64_t ofpacts_stub[1024 / 8];
> +    struct ofpbuf ofpacts;
>      struct flow flow;
>      enum ofperr error;
>
> @@ -2059,20 +2051,21 @@ handle_packet_out(struct ofconn *ofconn, const struct ofp_packet_out *opo)
>
>      error = reject_slave_controller(ofconn);
>      if (error) {
> -        return error;
> +        goto exit;
>      }
>
>      /* Decode message. */
> -    error = ofputil_decode_packet_out(&po, opo);
> +    ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
> +    error = ofputil_decode_packet_out(&po, opo, &ofpacts);
>      if (error) {
> -        return error;
> +        goto exit_free_ofpacts;
>      }
>
>      /* Get payload. */
>      if (po.buffer_id != UINT32_MAX) {
>          error = ofconn_pktbuf_retrieve(ofconn, po.buffer_id, &payload, NULL);
>          if (error || !payload) {
> -            return error;
> +            goto exit_free_ofpacts;
>          }
>      } else {
>          payload = xmalloc(sizeof *payload);
> @@ -2081,10 +2074,12 @@ handle_packet_out(struct ofconn *ofconn, const struct ofp_packet_out *opo)
>
>      /* Send out packet. */
>      flow_extract(payload, 0, 0, po.in_port, &flow);
> -    error = p->ofproto_class->packet_out(p, payload, &flow,
> -                                         po.actions, po.n_actions);
> +    error = p->ofproto_class->packet_out(p, payload, &flow, po.ofpacts);
>      ofpbuf_delete(payload);
>
> +exit_free_ofpacts:
> +    ofpbuf_uninit(&ofpacts);
> +exit:
>      return error;
>  }
>
> @@ -2486,8 +2481,7 @@ handle_flow_stats_request(struct ofconn *ofconn,
>          fs.hard_age = age_secs(now - rule->modified);
>          ofproto->ofproto_class->rule_get_stats(rule, &fs.packet_count,
>                                                 &fs.byte_count);
> -        fs.actions = rule->actions;
> -        fs.n_actions = rule->n_actions;
> +        fs.ofpacts = rule->ofpacts;
>          ofputil_append_flow_stats_reply(&fs, &replies);
>      }
>      ofconn_send_replies(ofconn, &replies);
> @@ -2513,8 +2507,8 @@ flow_stats_ds(struct rule *rule, struct ds *results)
>      ds_put_format(results, "n_bytes=%"PRIu64", ", byte_count);
>      cls_rule_format(&rule->cr, results);
>      ds_put_char(results, ',');
> -    if (rule->n_actions > 0) {
> -        ofp_print_actions(results, rule->actions, rule->n_actions);
> +    if (rule->ofpacts->type != OFPACT_END) {
> +        ofpacts_format(rule->ofpacts, results);
>      } else {
>          ds_put_cstr(results, "drop");
>      }
> @@ -2763,6 +2757,9 @@ is_flow_deletion_pending(const struct ofproto *ofproto,
>   * error code on failure, or OFPROTO_POSTPONE if the operation cannot be
>   * initiated now but may be retried later.
>   *
> + * Upon successful return, takes ownership of 'fm->ofpacts'.  On failure,
> + * ownership remains with the caller.
> + *
>   * 'ofconn' is used to retrieve the packet buffer specified in ofm->buffer_id,
>   * if any. */
>  static enum ofperr
> @@ -2831,8 +2828,8 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn,
>      rule->hard_timeout = fm->hard_timeout;
>      rule->table_id = table - ofproto->tables;
>      rule->send_flow_removed = (fm->flags & OFPFF_SEND_FLOW_REM) != 0;
> -    rule->actions = ofputil_actions_clone(fm->actions, fm->n_actions);
> -    rule->n_actions = fm->n_actions;
> +    rule->ofpacts = xmemdup(fm->ofpacts, fm->ofpacts_len);
> +    rule->ofpacts_len = fm->ofpacts_len;
>      rule->evictable = true;
>      rule->eviction_group = NULL;
>
> @@ -2914,14 +2911,14 @@ modify_flows__(struct ofproto *ofproto, struct ofconn *ofconn,
>              continue;
>          }
>
> -        if (!ofputil_actions_equal(fm->actions, fm->n_actions,
> -                                   rule->actions, rule->n_actions)) {
> +        if (!ofpacts_equal(fm->ofpacts, fm->ofpacts_len,
> +                           rule->ofpacts, rule->ofpacts_len)) {
>              ofoperation_create(group, rule, OFOPERATION_MODIFY);
> -            rule->pending->actions = rule->actions;
> -            rule->pending->n_actions = rule->n_actions;
> -            rule->actions = ofputil_actions_clone(fm->actions, fm->n_actions);
> -            rule->n_actions = fm->n_actions;
> -            ofproto->ofproto_class->rule_modify_actions(rule);
> +            rule->pending->ofpacts = rule->ofpacts;
> +            rule->pending->ofpacts_len = rule->ofpacts_len;
> +            rule->ofpacts = xmemdup(fm->ofpacts, fm->ofpacts_len);
> +            rule->ofpacts_len = fm->ofpacts_len;
> +            rule->ofproto->ofproto_class->rule_modify_actions(rule);
>          } else {
>              rule->modified = time_msec();
>          }
> @@ -3119,30 +3116,35 @@ handle_flow_mod(struct ofconn *ofconn, const struct ofp_header *oh)
>  {
>      struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
>      struct ofputil_flow_mod fm;
> +    uint64_t ofpacts_stub[1024 / 8];
> +    struct ofpbuf ofpacts;
>      enum ofperr error;
>      long long int now;
>
>      error = reject_slave_controller(ofconn);
>      if (error) {
> -        return error;
> +        goto exit;
>      }
>
> -    error = ofputil_decode_flow_mod(&fm, oh, ofconn_get_protocol(ofconn));
> +    ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
> +    error = ofputil_decode_flow_mod(&fm, oh, ofconn_get_protocol(ofconn),
> +                                    &ofpacts);
>      if (error) {
> -        return error;
> +        goto exit_free_ofpacts;
>      }
>
>      /* We do not support the OpenFlow 1.0 emergency flow cache, which is not
>       * required in OpenFlow 1.0.1 and removed from OpenFlow 1.1. */
>      if (fm.flags & OFPFF_EMERG) {
> -        /* There isn't a good fit for an error code, so just state that the
> -         * flow table is full. */
> -        return OFPERR_OFPFMFC_ALL_TABLES_FULL;
> +        /* We do not support the emergency flow cache.  It will hopefully get
> +         * dropped from OpenFlow in the near future.  There is no good error
> +         * code, so just state that the flow table is full. */
> +        error = OFPERR_OFPFMFC_ALL_TABLES_FULL;
> +    } else {
> +        error = handle_flow_mod__(ofconn_get_ofproto(ofconn), ofconn, &fm, oh);
>      }
> -
> -    error = handle_flow_mod__(ofconn_get_ofproto(ofconn), ofconn, &fm, oh);
>      if (error) {
> -        return error;
> +        goto exit_free_ofpacts;
>      }
>
>      /* Record the operation for logging a summary report. */
> @@ -3171,7 +3173,10 @@ handle_flow_mod(struct ofconn *ofconn, const struct ofp_header *oh)
>      }
>      ofproto->last_op = now;
>
> -    return 0;
> +exit_free_ofpacts:
> +    ofpbuf_uninit(&ofpacts);
> +exit:
> +    return error;
>  }
>
>  static enum ofperr
> @@ -3607,7 +3612,7 @@ ofoperation_destroy(struct ofoperation *op)
>          hmap_remove(&group->ofproto->deletions, &op->hmap_node);
>      }
>      list_remove(&op->group_node);
> -    free(op->actions);
> +    free(op->ofpacts);
>      free(op);
>
>      if (list_is_empty(&group->ops) && !list_is_empty(&group->ofproto_node)) {
> @@ -3707,10 +3712,10 @@ ofoperation_complete(struct ofoperation *op, enum ofperr error)
>          if (!error) {
>              rule->modified = time_msec();
>          } else {
> -            free(rule->actions);
> -            rule->actions = op->actions;
> -            rule->n_actions = op->n_actions;
> -            op->actions = NULL;
> +            free(rule->ofpacts);
> +            rule->ofpacts = op->ofpacts;
> +            rule->ofpacts_len = op->ofpacts_len;
> +            op->ofpacts = NULL;
>          }
>          break;
>
> diff --git a/tests/learn.at b/tests/learn.at
> index 9304861..da82f51 100644
> --- a/tests/learn.at
> +++ b/tests/learn.at
> @@ -10,7 +10,7 @@ AT_CHECK([ovs-ofctl parse-flows flows.txt], [0],
>  [[usable protocols: any
>  chosen protocol: OpenFlow10-table_id
>  OFPT_FLOW_MOD (xid=0x1): ADD actions=learn(table=1)
> -OFPT_FLOW_MOD (xid=0x2): ADD actions=learn(table=1,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],output:NXM_OF_IN_PORT[],load:0x000a->NXM_NX_REG0[5..10])
> +OFPT_FLOW_MOD (xid=0x2): ADD actions=learn(table=1,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],output:NXM_OF_IN_PORT[],load:0xa->NXM_NX_REG0[5..10])
>  OFPT_FLOW_MOD (xid=0x3): ADD actions=learn(table=1,idle_timeout=10,hard_timeout=20,fin_idle_timeout=5,fin_hard_timeout=10,priority=10,cookie=0xfedcba9876543210,in_port=99,NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],load:NXM_OF_IN_PORT[]->NXM_NX_REG1[16..31])
>  ]])
>  AT_CLEANUP
> @@ -42,7 +42,7 @@ ip,actions=learn(eth_type=0x800,OXM_OF_IPV4_DST[])
>  AT_CHECK([ovs-ofctl parse-flows flows.txt], [0],
>  [[usable protocols: any
>  chosen protocol: OpenFlow10-table_id
> -OFPT_FLOW_MOD (xid=0x1): ADD actions=learn(table=1,eth_type=0x800,load:0x00000005->NXM_OF_IP_DST[])
> +OFPT_FLOW_MOD (xid=0x1): ADD actions=learn(table=1,eth_type=0x800,load:0x5->NXM_OF_IP_DST[])
>  OFPT_FLOW_MOD (xid=0x2): ADD ip actions=learn(table=1,load:NXM_OF_IP_DST[]->NXM_NX_REG1[])
>  OFPT_FLOW_MOD (xid=0x3): ADD ip actions=learn(table=1,eth_type=0x800,NXM_OF_IP_DST[])
>  ]])
> diff --git a/tests/test-bundle.c b/tests/test-bundle.c
> index 672c426..f2d9b82 100644
> --- a/tests/test-bundle.c
> +++ b/tests/test-bundle.c
> @@ -1,4 +1,4 @@
> -/* Copyright (c) 2011 Nicira, Inc.
> +/* Copyright (c) 2011, 2012 Nicira, Inc.
>   *
>   * Licensed under the Apache License, Version 2.0 (the "License");
>   * you may not use this file except in compliance with the License.
> @@ -21,6 +21,7 @@
>  #include <stdlib.h>
>
>  #include "flow.h"
> +#include "ofp-actions.h"
>  #include "ofpbuf.h"
>  #include "random.h"
>
> @@ -64,22 +65,24 @@ slave_enabled_cb(uint16_t slave_id, void *aux)
>      return slave ? slave->enabled : false;
>  }
>
> -static struct nx_action_bundle *
> +static struct ofpact_bundle *
>  parse_bundle_actions(char *actions)
>  {
> -    struct nx_action_bundle *nab;
> -    struct ofpbuf b;
> +    struct ofpact_bundle *bundle;
> +    struct ofpbuf ofpacts;
> +    struct ofpact *action;
>
> -    ofpbuf_init(&b, 0);
> -    bundle_parse_load(&b, actions);
> -    nab = ofpbuf_steal_data(&b);
> -    ofpbuf_uninit(&b);
> +    ofpbuf_init(&ofpacts, 0);
> +    bundle_parse_load(actions, &ofpacts);
> +    action = ofpacts.data;
> +    bundle = ofpact_get_BUNDLE(xmemdup(action, action->len));
> +    ofpbuf_uninit(&ofpacts);
>
> -    if (ntohs(nab->n_slaves) > MAX_SLAVES) {
> +    if (bundle->n_slaves > MAX_SLAVES) {
>          ovs_fatal(0, "At most %u slaves are supported", MAX_SLAVES);
>      }
>
> -    return nab;
> +    return bundle;
>  }
>
>  static const char *
> @@ -101,7 +104,7 @@ int
>  main(int argc, char *argv[])
>  {
>      bool ok = true;
> -    struct nx_action_bundle *nab;
> +    struct ofpact_bundle *bundle;
>      struct flow *flows;
>      size_t i, n_permute, old_n_enabled;
>      struct slave_group sg;
> @@ -114,12 +117,12 @@ main(int argc, char *argv[])
>          ovs_fatal(0, "usage: %s bundle_action", program_name);
>      }
>
> -    nab = parse_bundle_actions(argv[1]);
> +    bundle = parse_bundle_actions(argv[1]);
>
>      /* Generate 'slaves' array. */
>      sg.n_slaves = 0;
> -    for (i = 0; i < ntohs(nab->n_slaves); i++) {
> -        uint16_t slave_id = bundle_get_slave(nab, i);
> +    for (i = 0; i < bundle->n_slaves; i++) {
> +        uint16_t slave_id = bundle->slaves[i];
>
>          if (slave_lookup(&sg, slave_id)) {
>              ovs_fatal(0, "Redundant slaves are not supported. ");
> @@ -136,10 +139,6 @@ main(int argc, char *argv[])
>          flows[i].regs[0] = OFPP_NONE;
>      }
>
> -    if (bundle_check(nab, 1024, flows)) {
> -        ovs_fatal(0, "Bundle action fails to check.");
> -    }
> -
>      /* Cycles through each possible liveness permutation for the given
>       * n_slaves.  The initial state is equivalent to all slaves down, so we
>       * skip it by starting at i = 1. We do one extra iteration to cover
> @@ -188,23 +187,19 @@ main(int argc, char *argv[])
>              uint16_t old_slave_id, ofp_port;
>
>              old_slave_id = flow->regs[0];
> -            ofp_port = bundle_execute(nab, flow, slave_enabled_cb, &sg);
> -            bundle_execute_load(nab, flow, slave_enabled_cb, &sg);
> -            if (flow->regs[0] != ofp_port) {
> -                ovs_fatal(0, "bundle_execute_load() and bundle_execute() "
> -                          "disagree");
> -            }
> +            ofp_port = bundle_execute(bundle, flow, slave_enabled_cb, &sg);
> +            flow->regs[0] = ofp_port;
>
> -            if (flow->regs[0] != OFPP_NONE) {
> -                slave_lookup(&sg, flow->regs[0])->flow_count++;
> +            if (ofp_port != OFPP_NONE) {
> +                slave_lookup(&sg, ofp_port)->flow_count++;
>              }
>
> -            if (old_slave_id != flow->regs[0]) {
> +            if (old_slave_id != ofp_port) {
>                  changed++;
>              }
>          }
>
> -        if (nab->algorithm == htons(NX_BD_ALG_ACTIVE_BACKUP)) {
> +        if (bundle->algorithm == NX_BD_ALG_ACTIVE_BACKUP) {
>              perfect = active == old_active ? 0.0 : 1.0;
>          } else {
>              if (old_n_enabled || n_enabled) {
> @@ -229,7 +224,7 @@ main(int argc, char *argv[])
>              if (slave->enabled) {
>                  double perfect_fp;
>
> -                if (nab->algorithm == htons(NX_BD_ALG_ACTIVE_BACKUP)) {
> +                if (bundle->algorithm == NX_BD_ALG_ACTIVE_BACKUP) {
>                      perfect_fp = j == active ? 1.0 : 0.0;
>                  } else {
>                      perfect_fp = 1.0 / n_enabled;
> @@ -262,7 +257,7 @@ main(int argc, char *argv[])
>          old_n_enabled = n_enabled;
>      }
>
> -    free(nab);
> +    free(bundle);
>      free(flows);
>      return ok ? 0 : 1;
>  }
> diff --git a/tests/test-multipath.c b/tests/test-multipath.c
> index 483eb3d..8a35567 100644
> --- a/tests/test-multipath.c
> +++ b/tests/test-multipath.c
> @@ -1,5 +1,5 @@
>  /*
> - * Copyright (c) 2010 Nicira, Inc.
> + * Copyright (c) 2010, 2012 Nicira, Inc.
>   *
>   * Licensed under the Apache License, Version 2.0 (the "License");
>   * you may not use this file except in compliance with the License.
> @@ -25,6 +25,7 @@
>  #include <stdlib.h>
>
>  #include "flow.h"
> +#include "ofp-actions.h"
>  #include "random.h"
>  #include "util.h"
>
> @@ -32,7 +33,7 @@ int
>  main(int argc, char *argv[])
>  {
>      enum { MP_MAX_LINKS = 63 };
> -    struct nx_action_multipath mp;
> +    struct ofpact_multipath mp;
>      bool ok = true;
>      int n;
>
> @@ -60,11 +61,11 @@ main(int argc, char *argv[])
>
>              random_bytes(&flow, sizeof flow);
>
> -            mp.max_link = htons(n - 1);
> +            mp.max_link = n - 1;
>              multipath_execute(&mp, &flow);
>              old_link = flow.regs[0];
>
> -            mp.max_link = htons(n);
> +            mp.max_link = n;
>              multipath_execute(&mp, &flow);
>              new_link = flow.regs[0];
>
> @@ -91,7 +92,7 @@ main(int argc, char *argv[])
>                 "stddev/expected=%.4f\n",
>                 n, n + 1, disruption, perfect, distribution);
>
> -        switch (ntohs(mp.algorithm)) {
> +        switch (mp.algorithm) {
>          case NX_MP_ALG_MODULO_N:
>              if (disruption < (n < 2 ? .25 : .5)) {
>                  fprintf(stderr, "%d -> %d: disruption=%.2f < .5\n",
> diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
> index 7413455..e10b966 100644
> --- a/utilities/ovs-ofctl.c
> +++ b/utilities/ovs-ofctl.c
> @@ -35,9 +35,9 @@
>  #include "compiler.h"
>  #include "dirs.h"
>  #include "dynamic-string.h"
> -#include "netlink.h"
>  #include "nx-match.h"
>  #include "odp-util.h"
> +#include "ofp-actions.h"
>  #include "ofp-errors.h"
>  #include "ofp-parse.h"
>  #include "ofp-print.h"
> @@ -861,7 +861,7 @@ do_flow_mod__(const char *remote, struct ofputil_flow_mod *fms, size_t n_fms)
>          struct ofputil_flow_mod *fm = &fms[i];
>
>          transact_noreply(vconn, ofputil_encode_flow_mod(fm, protocol));
> -        free(fm->actions);
> +        free(fm->ofpacts);
>      }
>      vconn_close(vconn);
>  }
> @@ -1233,19 +1233,19 @@ static void
>  do_packet_out(int argc, char *argv[])
>  {
>      struct ofputil_packet_out po;
> -    struct ofpbuf actions;
> +    struct ofpbuf ofpacts;
>      struct vconn *vconn;
>      int i;
>
> -    ofpbuf_init(&actions, sizeof(union ofp_action));
> -    parse_ofp_actions(argv[3], &actions);
> +    ofpbuf_init(&ofpacts, 64);
> +    parse_ofpacts(argv[3], &ofpacts);
>
>      po.buffer_id = UINT32_MAX;
>      po.in_port = (!strcasecmp(argv[2], "none") ? OFPP_NONE
>                    : !strcasecmp(argv[2], "local") ? OFPP_LOCAL
>                    : str_to_port_no(argv[1], argv[2]));
> -    po.actions = actions.data;
> -    po.n_actions = actions.size / sizeof(union ofp_action);
> +    po.ofpacts = ofpacts.data;
> +    po.ofpacts_len = ofpacts.size;
>
>      open_vconn(argv[1], &vconn);
>      for (i = 4; i < argc; i++) {
> @@ -1264,7 +1264,7 @@ do_packet_out(int argc, char *argv[])
>          ofpbuf_delete(packet);
>      }
>      vconn_close(vconn);
> -    ofpbuf_uninit(&actions);
> +    ofpbuf_uninit(&ofpacts);
>  }
>
>  static void
> @@ -1482,8 +1482,8 @@ struct fte_version {
>      uint16_t idle_timeout;
>      uint16_t hard_timeout;
>      uint16_t flags;
> -    union ofp_action *actions;
> -    size_t n_actions;
> +    struct ofpact *ofpacts;
> +    size_t ofpacts_len;
>  };
>
>  /* Frees 'version' and the data that it owns. */
> @@ -1491,7 +1491,7 @@ static void
>  fte_version_free(struct fte_version *version)
>  {
>      if (version) {
> -        free(version->actions);
> +        free(version->ofpacts);
>          free(version);
>      }
>  }
> @@ -1506,9 +1506,8 @@ fte_version_equals(const struct fte_version *a, const struct fte_version *b)
>      return (a->cookie == b->cookie
>              && a->idle_timeout == b->idle_timeout
>              && a->hard_timeout == b->hard_timeout
> -            && a->n_actions == b->n_actions
> -            && !memcmp(a->actions, b->actions,
> -                       a->n_actions * sizeof *a->actions));
> +            && ofpacts_equal(a->ofpacts, a->ofpacts_len,
> +                             b->ofpacts, b->ofpacts_len));
>  }
>
>  /* Prints 'version' on stdout.  Expects the caller to have printed the rule
> @@ -1529,7 +1528,7 @@ fte_version_print(const struct fte_version *version)
>      }
>
>      ds_init(&s);
> -    ofp_print_actions(&s, version->actions, version->n_actions);
> +    ofpacts_format(version->ofpacts, &s);
>      printf(" %s\n", ds_cstr(&s));
>      ds_destroy(&s);
>  }
> @@ -1617,8 +1616,8 @@ read_flows_from_file(const char *filename, struct classifier *cls, int index)
>          version->idle_timeout = fm.idle_timeout;
>          version->hard_timeout = fm.hard_timeout;
>          version->flags = fm.flags & (OFPFF_SEND_FLOW_REM | OFPFF_EMERG);
> -        version->actions = fm.actions;
> -        version->n_actions = fm.n_actions;
> +        version->ofpacts = fm.ofpacts;
> +        version->ofpacts_len = fm.ofpacts_len;
>
>          usable_protocols &= ofputil_usable_protocols(&fm.cr);
>
> @@ -1684,10 +1683,14 @@ read_flows_from_switch(struct vconn *vconn,
>              for (;;) {
>                  struct fte_version *version;
>                  struct ofputil_flow_stats fs;
> +                struct ofpbuf ofpacts;
>                  int retval;
>
> -                retval = ofputil_decode_flow_stats_reply(&fs, reply, false);
> +                ofpbuf_init(&ofpacts, 64);
> +                retval = ofputil_decode_flow_stats_reply(&fs, reply, false,
> +                                                         &ofpacts);
>                  if (retval) {
> +                    ofpbuf_uninit(&ofpacts);
>                      if (retval != EOF) {
>                          ovs_fatal(0, "parse error in reply");
>                      }
> @@ -1699,9 +1702,8 @@ read_flows_from_switch(struct vconn *vconn,
>                  version->idle_timeout = fs.idle_timeout;
>                  version->hard_timeout = fs.hard_timeout;
>                  version->flags = 0;
> -                version->n_actions = fs.n_actions;
> -                version->actions = xmemdup(fs.actions,
> -                                           fs.n_actions * sizeof *fs.actions);
> +                version->ofpacts = ofpbuf_steal_data(&ofpacts);
> +                version->ofpacts_len = ofpacts.size;
>
>                  fte_insert(cls, &fs.rule, version, index);
>              }
> @@ -1734,11 +1736,11 @@ fte_make_flow_mod(const struct fte *fte, int index, uint16_t command,
>      fm.flags = version->flags;
>      if (command == OFPFC_ADD || command == OFPFC_MODIFY ||
>          command == OFPFC_MODIFY_STRICT) {
> -        fm.actions = version->actions;
> -        fm.n_actions = version->n_actions;
> +        fm.ofpacts = version->ofpacts;
> +        fm.ofpacts_len = version->ofpacts_len;
>      } else {
> -        fm.actions = NULL;
> -        fm.n_actions = 0;
> +        fm.ofpacts = NULL;
> +        fm.ofpacts_len = 0;
>      }
>
>      ofm = ofputil_encode_flow_mod(&fm, protocol);
> @@ -1891,7 +1893,7 @@ do_parse_flows__(struct ofputil_flow_mod *fms, size_t n_fms)
>          ofp_print(stdout, msg->data, msg->size, verbosity);
>          ofpbuf_delete(msg);
>
> -        free(fm->actions);
> +        free(fm->ofpacts);
>      }
>  }
>
> --
> 1.7.2.5
>
> _______________________________________________
> dev mailing list
> dev at openvswitch.org
> http://openvswitch.org/mailman/listinfo/dev



More information about the dev mailing list