set_field_parse__(char *arg, const struct ofpact_parse_params *pp)
{
    char *value;
    char *delim;
    char *key;
    const struct mf_field *mf;
    union mf_value sf_value, sf_mask;
    char *error;

    error = set_field_split_str(arg, &key, &value, &delim);
    if (error) {
        return error;
    }

    mf = mf_from_name(key);
    if (!mf) {
        return xasprintf("%s is not a valid OXM field name", key);
    }
    if (!mf->writable) {
        return xasprintf("%s is read-only", key);
    }

    delim[0] = '\0';
    error = mf_parse(mf, value, pp->port_map, &sf_value, &sf_mask);
    if (error) {
        return error;
    }

    if (!mf_is_value_valid(mf, &sf_value)) {
        return xasprintf("%s is not a valid value for field %s", value, key);
    }

    *pp->usable_protocols &= mf->usable_protocols_exact;

    ofpact_put_set_field(pp->ofpacts, mf, &sf_value, &sf_mask);
    return NULL;
}


log_bad_action(const struct ofp_action_header *actions, size_t actions_len,
               const struct ofp_action_header *bad_action, enum ofperr error)
{
    if (!VLOG_DROP_WARN(&rl)) {
        struct ds s;

        ds_init(&s);
        ds_put_hex_dump(&s, actions, actions_len, 0, false);
        VLOG_WARN("bad action at offset %#"PRIxPTR" (%s):\n%s",
                  (char *)bad_action - (char *)actions,
                  ofperr_get_name(error), ds_cstr(&s));
        ds_destroy(&s);
    }
}

ofp-actions: Fix use-after-free while decoding RAW_ENCAP.

While decoding RAW_ENCAP action, decode_ed_prop() might re-allocate
ofpbuf if there is no enough space left.  However, function
'decode_NXAST_RAW_ENCAP' continues to use old pointer to 'encap'
structure leading to write-after-free and incorrect decoding.

  ==3549105==ERROR: AddressSanitizer: heap-use-after-free on address
  0x60600000011a at pc 0x0000005f6cc6 bp 0x7ffc3a2d4410 sp 0x7ffc3a2d4408
  WRITE of size 2 at 0x60600000011a thread T0
    #0 0x5f6cc5 in decode_NXAST_RAW_ENCAP lib/ofp-actions.c:4461:20
    #1 0x5f0551 in ofpact_decode ./lib/ofp-actions.inc2:4777:16
    #2 0x5ed17c in ofpacts_decode lib/ofp-actions.c:7752:21
    #3 0x5eba9a in ofpacts_pull_openflow_actions__ lib/ofp-actions.c:7791:13
    #4 0x5eb9fc in ofpacts_pull_openflow_actions lib/ofp-actions.c:7835:12
    #5 0x64bb8b in ofputil_decode_packet_out lib/ofp-packet.c:1113:17
    #6 0x65b6f4 in ofp_print_packet_out lib/ofp-print.c:148:13
    #7 0x659e3f in ofp_to_string__ lib/ofp-print.c:1029:16
    #8 0x659b24 in ofp_to_string lib/ofp-print.c:1244:21
    #9 0x65a28c in ofp_print lib/ofp-print.c:1288:28
    #10 0x540d11 in ofctl_ofp_parse utilities/ovs-ofctl.c:2814:9
    #11 0x564228 in ovs_cmdl_run_command__ lib/command-line.c:247:17
    #12 0x56408a in ovs_cmdl_run_command lib/command-line.c:278:5
    #13 0x5391ae in main utilities/ovs-ofctl.c:179:9
    #14 0x7f6911ce9081 in __libc_start_main (/lib64/libc.so.6+0x27081)
    #15 0x461fed in _start (utilities/ovs-ofctl+0x461fed)

Fix that by getting a new pointer before using.

Credit to OSS-Fuzz.

Fuzzer regression test will fail only with AddressSanitizer enabled.

Reported-at: https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=27851
Fixes: f839892a206a ("OF support and translation of generic encap and decap")
Acked-by: William Tu <u9012063@gmail.com>
Signed-off-by: Ilya Maximets <i.maximets@ovn.org>


parse_LEARN(char *arg, const struct ofpact_parse_params *pp)
{
    return learn_parse(arg, pp->port_map, pp->table_map, pp->ofpacts);
}


ofpact_put_raw(struct ofpbuf *buf, enum ofp_version ofp_version,
               enum ofp_raw_action_type raw, uint64_t arg)
{
    const struct ofpact_raw_instance *inst;
    struct ofp_action_header *oah;
    const struct ofpact_hdrs *hdrs;

    inst = ofpact_raw_lookup(ofp_version, raw);
    hdrs = &inst->hdrs;

    oah = ofpbuf_put_zeros(buf, inst->min_length);
    oah->type = htons(hdrs->vendor ? OFPAT_VENDOR : hdrs->type);
    oah->len = htons(inst->min_length);
    oah->vendor = htonl(hdrs->vendor);

    switch (hdrs->vendor) {
    case 0:
        break;

    case NX_VENDOR_ID:
    case ONF_VENDOR_ID: {
        struct ext_action_header *nah = (struct ext_action_header *) oah;
        nah->subtype = htons(hdrs->type);
        break;
    }

    default:
        OVS_NOT_REACHED();
    }

    if (inst->arg_len) {
        uint8_t *p = (uint8_t *) oah + inst->arg_ofs + inst->arg_len;
        int i;

        for (i = 0; i < inst->arg_len; i++) {
            *--p = arg;
            arg >>= 8;
        }
    } else {
        ovs_assert(!arg);
    }

    return oah;
}


decode_NXAST_RAW_LEARN2(const struct nx_action_learn2 *nal,
                        enum ofp_version ofp_version OVS_UNUSED,
                        const struct vl_mff_map *vl_mff_map,
                        uint64_t *tlv_bitmap, struct ofpbuf *ofpacts)
{
    struct ofpbuf b = ofpbuf_const_initializer(nal, ntohs(nal->up.len));
    struct ofpact_learn *learn;
    enum ofperr error;

    if (nal->pad2) {
        return OFPERR_NXBAC_MUST_BE_ZERO;
    }

    learn = ofpact_put_LEARN(ofpacts);
    error = decode_LEARN_common(&nal->up, NXAST_RAW_LEARN2, learn);
    if (error) {
        return error;
    }

    learn->limit = ntohl(nal->limit);

    if (learn->flags & ~(NX_LEARN_F_SEND_FLOW_REM |
                         NX_LEARN_F_DELETE_LEARNED |
                         NX_LEARN_F_WRITE_RESULT)) {
        return OFPERR_OFPBAC_BAD_ARGUMENT;
    }

    ofpbuf_pull(&b, sizeof *nal);

    if (learn->flags & NX_LEARN_F_WRITE_RESULT) {
        error = nx_pull_header(&b, vl_mff_map, &learn->result_dst.field, NULL);
        if (error) {
            return error;
        }
        if (!learn->result_dst.field->writable) {
            return OFPERR_OFPBAC_BAD_SET_ARGUMENT;
        }
        learn->result_dst.ofs = ntohs(nal->result_dst_ofs);
        learn->result_dst.n_bits = 1;
    } else if (nal->result_dst_ofs) {
        return OFPERR_OFPBAC_BAD_ARGUMENT;
    }

    return decode_LEARN_specs(b.data, (char *) nal + ntohs(nal->up.len),
                              vl_mff_map, tlv_bitmap, ofpacts);
}


check_set_ip(struct ofpact_check_params *cp)
{
    if (!is_ip_any(&cp->match->flow)) {
        inconsistent_match(&cp->usable_protocols);
    }
    return 0;
}


format_CT(const struct ofpact_conntrack *a,
          const struct ofpact_format_params *fp)
{
    ds_put_format(fp->s, "%sct(%s", colors.paren, colors.end);
    if (a->flags & NX_CT_F_COMMIT) {
        ds_put_format(fp->s, "%scommit%s,", colors.value, colors.end);
    }
    if (a->flags & NX_CT_F_FORCE) {
        ds_put_format(fp->s, "%sforce%s,", colors.value, colors.end);
    }
    if (a->recirc_table != NX_CT_RECIRC_NONE) {
        ds_put_format(fp->s, "%stable=%s", colors.special, colors.end);
        ofputil_format_table(a->recirc_table, fp->table_map, fp->s);
        ds_put_char(fp->s, ',');
    }
    if (a->zone_src.field) {
        ds_put_format(fp->s, "%szone=%s", colors.param, colors.end);
        mf_format_subfield(&a->zone_src, fp->s);
        ds_put_char(fp->s, ',');
    } else if (a->zone_imm) {
        ds_put_format(fp->s, "%szone=%s%"PRIu16",",
                      colors.param, colors.end, a->zone_imm);
    }
    /* If the first action is a NAT action, format it outside of the 'exec'
     * envelope. */
    const struct ofpact *action = a->actions;
    size_t actions_len = ofpact_ct_get_action_len(a);
    if (actions_len && action->type == OFPACT_NAT) {
        format_NAT(ofpact_get_NAT(action), fp);
        ds_put_char(fp->s, ',');
        actions_len -= OFPACT_ALIGN(action->len);
        action = ofpact_next(action);
    }
    if (actions_len) {
        ds_put_format(fp->s, "%sexec(%s", colors.paren, colors.end);
        ofpacts_format(action, actions_len, fp);
        ds_put_format(fp->s, "%s),%s", colors.paren, colors.end);
    }
    format_alg(a->alg, fp->s);
    ds_chomp(fp->s, ',');
    ds_put_format(fp->s, "%s)%s", colors.paren, colors.end);
}


format_GROUP(const struct ofpact_group *a,
             const struct ofpact_format_params *fp)
{
    ds_put_format(fp->s, "%sgroup:%s%"PRIu32,
                  colors.special, colors.end, a->group_id);
}


decode_NXAST_RAW_CLONE(const struct ext_action_header *eah,
                       enum ofp_version ofp_version,
                       const struct vl_mff_map *vl_mff_map,
                       uint64_t *tlv_bitmap, struct ofpbuf *out)
{
    int error;
    struct ofpbuf openflow;
    const size_t clone_offset = ofpacts_pull(out);
    struct ofpact_nest *clone = ofpact_put_CLONE(out);

    /* decode action list */
    ofpbuf_pull(out, sizeof(*clone));
    openflow = ofpbuf_const_initializer(
                    eah + 1, ntohs(eah->len) - sizeof *eah);
    error = ofpacts_pull_openflow_actions__(&openflow, openflow.size,
                                            ofp_version,
                                            1u << OVSINST_OFPIT11_APPLY_ACTIONS,
                                            out, 0, vl_mff_map, tlv_bitmap);
    if (error) {
        return error;
    }
    clone = ofpbuf_push_uninit(out, sizeof *clone);
    out->header = &clone->ofpact;
    ofpact_finish_CLONE(out, &clone);
    ofpbuf_push_uninit(out, clone_offset);
    return error;
}


check_PUSH_MPLS(const struct ofpact_push_mpls *a,
                struct ofpact_check_params *cp)
{
    struct flow *flow = &cp->match->flow;

    if (flow->packet_type != htonl(PT_ETH)) {
        inconsistent_match(&cp->usable_protocols);
    }
    flow->dl_type = a->ethertype;

    /* The packet is now MPLS and the MPLS payload is opaque.
     * Thus nothing can be assumed about the network protocol.
     * Temporarily mark that we have no nw_proto. */
    flow->nw_proto = 0;

    return 0;
}


parse_DEBUG_RECIRC(char *arg OVS_UNUSED, const struct ofpact_parse_params *pp)
{
    ofpact_put_DEBUG_RECIRC(pp->ofpacts);
    return NULL;
}


parse_SET_L4_DST_PORT(char *arg, const struct ofpact_parse_params *pp)
{
    return str_to_u16(arg, "destination port",
                      &ofpact_put_SET_L4_DST_PORT(pp->ofpacts)->port);
}


decode_copy_field__(ovs_be16 src_offset, ovs_be16 dst_offset, ovs_be16 n_bits,
                    const void *action, ovs_be16 action_len, size_t oxm_offset,
                    const struct vl_mff_map *vl_mff_map,
                    uint64_t *tlv_bitmap, struct ofpbuf *ofpacts)
{
    struct ofpact_reg_move *move = ofpact_put_REG_MOVE(ofpacts);
    enum ofperr error;

    move->ofpact.raw = ONFACT_RAW13_COPY_FIELD;
    move->src.ofs = ntohs(src_offset);
    move->src.n_bits = ntohs(n_bits);
    move->dst.ofs = ntohs(dst_offset);
    move->dst.n_bits = ntohs(n_bits);

    struct ofpbuf b = ofpbuf_const_initializer(action, ntohs(action_len));
    ofpbuf_pull(&b, oxm_offset);

    error = mf_vl_mff_nx_pull_header(&b, vl_mff_map, &move->src.field, NULL,
                                     tlv_bitmap);
    if (error) {
        return error;
    }
    error = mf_vl_mff_nx_pull_header(&b, vl_mff_map, &move->dst.field, NULL,
                                     tlv_bitmap);
    if (error) {
        return error;
    }

    if (!is_all_zeros(b.data, b.size)) {
        return OFPERR_NXBRC_MUST_BE_ZERO;
    }

    return nxm_reg_move_check(move, NULL);
}


check_BUNDLE(const struct ofpact_bundle *a,
             const struct ofpact_check_params *cp)
{
    return bundle_check(a, cp->max_ports, cp->match);
}


encode_DEC_TTL(const struct ofpact_cnt_ids *dec_ttl,
               enum ofp_version ofp_version, struct ofpbuf *out)
{
    if (dec_ttl->ofpact.raw == NXAST_RAW_DEC_TTL_CNT_IDS
        || dec_ttl->n_controllers != 1
        || dec_ttl->cnt_ids[0] != 0) {
        struct nx_action_cnt_ids *nac_ids = put_NXAST_DEC_TTL_CNT_IDS(out);
        int ids_len = ROUND_UP(2 * dec_ttl->n_controllers, OFP_ACTION_ALIGN);
        ovs_be16 *ids;
        size_t i;

        nac_ids->len = htons(ntohs(nac_ids->len) + ids_len);
        nac_ids->n_controllers = htons(dec_ttl->n_controllers);

        ids = ofpbuf_put_zeros(out, ids_len);
        for (i = 0; i < dec_ttl->n_controllers; i++) {
            ids[i] = htons(dec_ttl->cnt_ids[i]);
        }
    } else {
        put_OFPAT_DEC_NW_TTL(out, ofp_version);
    }
}


ofpacts_check_consistency(struct ofpact ofpacts[], size_t ofpacts_len,
                          enum ofputil_protocol needed_protocols,
                          struct ofpact_check_params *cp)
{
    enum ofperr error = ofpacts_check(ofpacts, ofpacts_len, cp);
    if (!error && needed_protocols & ~cp->usable_protocols) {
        return OFPERR_OFPBAC_MATCH_INCONSISTENT;
    }
    return error;
}


decode_NXAST_RAW_STACK_POP(const struct nx_action_stack *nasp,
                           enum ofp_version ofp_version OVS_UNUSED,
                           const struct vl_mff_map *vl_mff_map,
                           uint64_t *tlv_bitmap, struct ofpbuf *ofpacts)
{
    struct ofpact_stack *pop = ofpact_put_STACK_POP(ofpacts);
    enum ofperr error = decode_stack_action(nasp, vl_mff_map, tlv_bitmap,
                                            pop);
    return error ? error : nxm_stack_pop_check(pop, NULL);
}


parse_REG_MOVE(const char *arg, const struct ofpact_parse_params *pp)
{
    struct ofpact_reg_move *move = ofpact_put_REG_MOVE(pp->ofpacts);
    return nxm_parse_reg_move(move, arg);
}


check_GOTO_TABLE(const struct ofpact_goto_table *a,
                 const struct ofpact_check_params *cp)
{
    if ((cp->table_id != 255 && a->table_id <= cp->table_id)
        || (cp->n_tables != 255 && a->table_id >= cp->n_tables)) {
        return OFPERR_OFPBIC_BAD_TABLE_ID;
    }
    return 0;
}


parse_ENQUEUE(char *arg, const struct ofpact_parse_params *pp)
{
    char *sp = NULL;
    char *port = strtok_r(arg, ":q,", &sp);
    char *queue = strtok_r(NULL, "", &sp);
    struct ofpact_enqueue *enqueue;

    if (port == NULL || queue == NULL) {
        return xstrdup("\"enqueue\" syntax is \"enqueue:PORT:QUEUE\" or "
                       "\"enqueue(PORT,QUEUE)\"");
    }

    enqueue = ofpact_put_ENQUEUE(pp->ofpacts);
    if (!ofputil_port_from_string(port, pp->port_map, &enqueue->port)) {
        return xasprintf("%s: enqueue to unknown port", port);
    }
    return str_to_u32(queue, &enqueue->queue);
}


format_EXIT(const struct ofpact_null *a OVS_UNUSED,
            const struct ofpact_format_params *fp)
{
    ds_put_format(fp->s, "%sexit%s", colors.special, colors.end);
}


check_subactions(struct ofpact *ofpacts, size_t ofpacts_len,
                 struct ofpact_check_params *cp)
{
    struct ofpact_check_params sub = *cp;
    enum ofperr error = ofpacts_check(ofpacts, ofpacts_len, &sub);
    cp->usable_protocols &= sub.usable_protocols;
    return error;
}


encode_CT_CLEAR(const struct ofpact_null *null OVS_UNUSED,
                enum ofp_version ofp_version OVS_UNUSED,
                struct ofpbuf *out)
{
    put_NXAST_CT_CLEAR(out);
}


format_STACK_PUSH(const struct ofpact_stack *a,
                  const struct ofpact_format_params *fp)
{
    nxm_format_stack_push(a, fp->s);
}


decode_NXAST_RAW_RESUBMIT_TABLE_CT(const struct nx_action_resubmit *nar,
                                   enum ofp_version ofp_version OVS_UNUSED,
                                   struct ofpbuf *out)
{
    enum ofperr error = decode_NXAST_RAW_RESUBMIT_TABLE(nar, ofp_version, out);
    if (error) {
        return error;
    }
    struct ofpact_resubmit *resubmit = out->header;
    resubmit->ofpact.raw = NXAST_RAW_RESUBMIT_TABLE_CT;
    resubmit->with_ct_orig = true;
    return 0;
}


decode_NXAST_RAW_DEC_TTL_CNT_IDS(const struct nx_action_cnt_ids *nac_ids,
                                 enum ofp_version ofp_version OVS_UNUSED,
                                 struct ofpbuf *out)
{
    struct ofpact_cnt_ids *ids;
    size_t ids_size;
    int i;

    ids = ofpact_put_DEC_TTL(out);
    ids->ofpact.raw = NXAST_RAW_DEC_TTL_CNT_IDS;
    ids->n_controllers = ntohs(nac_ids->n_controllers);
    ids_size = ntohs(nac_ids->len) - sizeof *nac_ids;

    if (!is_all_zeros(nac_ids->zeros, sizeof nac_ids->zeros)) {
        return OFPERR_NXBRC_MUST_BE_ZERO;
    }

    if (ids_size < ids->n_controllers * sizeof(ovs_be16)) {
        VLOG_WARN_RL(&rl, "Nicira action dec_ttl_cnt_ids only has %"PRIuSIZE" "
                     "bytes allocated for controller ids.  %"PRIuSIZE" bytes "
                     "are required for %u controllers.",
                     ids_size, ids->n_controllers * sizeof(ovs_be16),
                     ids->n_controllers);
        return OFPERR_OFPBAC_BAD_LEN;
    }

    for (i = 0; i < ids->n_controllers; i++) {
        uint16_t id = ntohs(((ovs_be16 *)(nac_ids + 1))[i]);
        ofpbuf_put(out, &id, sizeof id);
        ids = out->header;
    }

    ofpact_finish_DEC_TTL(out, &ids);

    return 0;
}


check_SET_MPLS_TTL(const struct ofpact_mpls_ttl *a OVS_UNUSED,
                   struct ofpact_check_params *cp)
{
    return check_set_mpls(cp);
}


encode_DEC_NSH_TTL(const struct ofpact_null *null OVS_UNUSED,
            enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out)
{
    put_NXAST_DEC_NSH_TTL(out);
}


decode_openflow11_instruction(const struct ofp11_instruction *inst,
                              enum ovs_instruction_type *type)
{
    uint16_t len = ntohs(inst->len);

    switch (inst->type) {
    case CONSTANT_HTONS(OFPIT11_EXPERIMENTER):
        return OFPERR_OFPBIC_BAD_EXPERIMENTER;

#define DEFINE_INST(ENUM, STRUCT, EXTENSIBLE, NAME)     \
        case CONSTANT_HTONS(ENUM):                      \
            if (EXTENSIBLE                              \
                ? len >= sizeof(struct STRUCT)          \
                : len == sizeof(struct STRUCT)) {       \
                *type = OVSINST_##ENUM;                 \
                return 0;                               \
            } else {                                    \
                return OFPERR_OFPBIC_BAD_LEN;           \
            }
OVS_INSTRUCTIONS
#undef DEFINE_INST

    default:
        return OFPERR_OFPBIC_UNKNOWN_INST;
    }
}


check_DEC_MPLS_TTL(const struct ofpact_null *a OVS_UNUSED,
                   struct ofpact_check_params *cp)
{
    return check_set_mpls(cp);
}


check_UNROLL_XLATE(const struct ofpact_unroll_xlate *a OVS_UNUSED,
                   const struct ofpact_check_params *cp OVS_UNUSED)
{
    /* UNROLL is an internal action that should never be seen via OpenFlow. */
    return OFPERR_OFPBAC_BAD_TYPE;
}


encode_SET_ETH_addr(const struct ofpact_mac *mac, enum ofp_version ofp_version,
                    enum ofp_raw_action_type raw, enum mf_field_id field,
                    struct ofpbuf *out)
{
    if (ofp_version < OFP12_VERSION) {
        struct ofp_action_dl_addr *oada;

        oada = ofpact_put_raw(out, ofp_version, raw, 0);
        oada->dl_addr = mac->mac;
    } else {
        put_set_field(out, ofp_version, field, eth_addr_to_uint64(mac->mac));
    }
}


check_CLONE(struct ofpact_nest *a, struct ofpact_check_params *cp)
{
    return check_subactions(a->actions, ofpact_nest_get_action_len(a), cp);
}


decode_NXAST_RAW_DECAP(const struct nx_action_decap *nad,
                       enum ofp_version ofp_version OVS_UNUSED,
                       struct ofpbuf *ofpacts)
{
    struct ofpact_decap * decap;

    if (ntohs(nad->len) > sizeof *nad) {
        /* No properties supported yet. */
        return OFPERR_NXBAC_UNKNOWN_ED_PROP;
    }

    decap = ofpact_put_DECAP(ofpacts);
    decap->ofpact.raw = NXAST_RAW_DECAP;
    decap->new_pkt_type = nad->new_pkt_type;
    return 0;
}


decode_NXAST_RAW_SET_TUNNEL64(uint64_t tun_id,
                              enum ofp_version ofp_version OVS_UNUSED,
                              struct ofpbuf *out)
{
    struct ofpact_tunnel *tunnel = ofpact_put_SET_TUNNEL(out);
    tunnel->ofpact.raw = NXAST_RAW_SET_TUNNEL64;
    tunnel->tun_id = tun_id;
    return 0;
}


ofpact_pull_raw(struct ofpbuf *buf, enum ofp_version ofp_version,
                enum ofp_raw_action_type *raw, uint64_t *arg)
{
    const struct ofp_action_header *oah = buf->data;
    const struct ofpact_raw_instance *action;
    unsigned int length;
    enum ofperr error;

    *raw = *arg = 0;
    error = ofpact_decode_raw(ofp_version, oah, buf->size, &action);
    if (error) {
        return error;
    }

    if (action->deprecation) {
        VLOG_INFO_RL(&rl, "%s is deprecated in %s (%s)",
                     action->name, ofputil_version_to_string(ofp_version),
                     action->deprecation);
    }

    length = ntohs(oah->len);
    if (length > buf->size) {
        VLOG_WARN_RL(&rl, "OpenFlow action %s length %u exceeds action buffer "
                     "length %"PRIu32, action->name, length, buf->size);
        return OFPERR_OFPBAC_BAD_LEN;
    }
    if (length < action->min_length || length > action->max_length) {
        VLOG_WARN_RL(&rl, "OpenFlow action %s length %u not in valid range "
                     "[%hu,%hu]", action->name, length,
                     action->min_length, action->max_length);
        return OFPERR_OFPBAC_BAD_LEN;
    }
    if (length % 8) {
        VLOG_WARN_RL(&rl, "OpenFlow action %s length %u is not a multiple "
                     "of 8", action->name, length);
        return OFPERR_OFPBAC_BAD_LEN;
    }

    *raw = action->raw;
    *arg = 0;
    if (action->arg_len) {
        const uint8_t *p;
        int i;

        p = ofpbuf_at_assert(buf, action->arg_ofs, action->arg_len);
        for (i = 0; i < action->arg_len; i++) {
            *arg = (*arg << 8) | p[i];
        }
    }

    ofpbuf_pull(buf, length);

    return 0;
}


parse_SET_IP_DSCP(char *arg, const struct ofpact_parse_params *pp)

{
    uint8_t tos;
    char *error;

    error = str_to_u8(arg, "TOS", &tos);
    if (error) {
        return error;
    }

    if (tos & ~IP_DSCP_MASK) {
        return xasprintf("%s: not a valid TOS", arg);
    }
    ofpact_put_SET_IP_DSCP(pp->ofpacts)->dscp = tos;
    return NULL;
}


check_SET_L4_SRC_PORT(struct ofpact_l4_port *a, struct ofpact_check_params *cp)
{
    return check_set_l4_port(a, cp);
}


encode_WRITE_METADATA(const struct ofpact_metadata *metadata,
                      enum ofp_version ofp_version, struct ofpbuf *out)
{
    if (ofp_version == OFP10_VERSION) {
        struct nx_action_write_metadata *nawm;

        nawm = put_NXAST_WRITE_METADATA(out);
        nawm->metadata = metadata->metadata;
        nawm->mask = metadata->mask;
    } else {
        struct ofp11_instruction_write_metadata *oiwm;

        oiwm = instruction_put_OFPIT11_WRITE_METADATA(out);
        oiwm->metadata = metadata->metadata;
        oiwm->metadata_mask = metadata->mask;
    }
}


parse_SET_MPLS_LABEL(char *arg, const struct ofpact_parse_params *pp)
{
    struct ofpact_mpls_label *mpls_label
        = ofpact_put_SET_MPLS_LABEL(pp->ofpacts);
    if (*arg == '\0') {
        return xstrdup("set_mpls_label: expected label.");
    }

    mpls_label->label = htonl(atoi(arg));
    return NULL;
}


encode_PUSH_VLAN(const struct ofpact_push_vlan *push_vlan,
                 enum ofp_version ofp_version, struct ofpbuf *out)
{
    if (ofp_version == OFP10_VERSION) {
        /* PUSH is a side effect of a SET_VLAN_VID/PCP, which should
         * follow this action. */
    } else {
        put_OFPAT11_PUSH_VLAN(out, push_vlan->ethertype);
    }
}


decode_NXAST_RAW_NAT(const struct nx_action_nat *nan,
                     enum ofp_version ofp_version OVS_UNUSED,
                     struct ofpbuf *out)
{
    struct ofpact_nat *nat;
    uint16_t range_present = ntohs(nan->range_present);
    const char *opts = (char *)(nan + 1);
    uint16_t len = ntohs(nan->len) - sizeof *nan;

    nat = ofpact_put_NAT(out);
    nat->flags = ntohs(nan->flags);

    /* Check for unknown or mutually exclusive flags. */
    if ((nat->flags & ~NX_NAT_F_MASK)
        || (nat->flags & NX_NAT_F_SRC && nat->flags & NX_NAT_F_DST)
        || (nat->flags & NX_NAT_F_PROTO_HASH
            && nat->flags & NX_NAT_F_PROTO_RANDOM)) {
        return OFPERR_OFPBAC_BAD_ARGUMENT;
    }

#define NX_NAT_GET_OPT(DST, SRC, LEN, TYPE)                     \
    (LEN >= sizeof(TYPE)                                        \
     ? (memcpy(DST, SRC, sizeof(TYPE)), LEN -= sizeof(TYPE),    \
        SRC += sizeof(TYPE))                                    \
     : NULL)

    nat->range_af = AF_UNSPEC;
    if (range_present & NX_NAT_RANGE_IPV4_MIN) {
        if (range_present & (NX_NAT_RANGE_IPV6_MIN | NX_NAT_RANGE_IPV6_MAX)) {
            return OFPERR_OFPBAC_BAD_ARGUMENT;
        }

        if (!NX_NAT_GET_OPT(&nat->range.addr.ipv4.min, opts, len, ovs_be32)
            || !nat->range.addr.ipv4.min) {
            return OFPERR_OFPBAC_BAD_ARGUMENT;
        }

        nat->range_af = AF_INET;

        if (range_present & NX_NAT_RANGE_IPV4_MAX) {
            if (!NX_NAT_GET_OPT(&nat->range.addr.ipv4.max, opts, len,
                                ovs_be32)) {
                return OFPERR_OFPBAC_BAD_ARGUMENT;
            }
            if (ntohl(nat->range.addr.ipv4.max)
                < ntohl(nat->range.addr.ipv4.min)) {
                return OFPERR_OFPBAC_BAD_ARGUMENT;
            }
        }
    } else if (range_present & NX_NAT_RANGE_IPV4_MAX) {
        return OFPERR_OFPBAC_BAD_ARGUMENT;
    } else if (range_present & NX_NAT_RANGE_IPV6_MIN) {
        if (!NX_NAT_GET_OPT(&nat->range.addr.ipv6.min, opts, len,
                            struct in6_addr)
            || ipv6_mask_is_any(&nat->range.addr.ipv6.min)) {
            return OFPERR_OFPBAC_BAD_ARGUMENT;
        }

        nat->range_af = AF_INET6;

        if (range_present & NX_NAT_RANGE_IPV6_MAX) {
            if (!NX_NAT_GET_OPT(&nat->range.addr.ipv6.max, opts, len,
                                struct in6_addr)) {
                return OFPERR_OFPBAC_BAD_ARGUMENT;
            }
            if (memcmp(&nat->range.addr.ipv6.max, &nat->range.addr.ipv6.min,
                       sizeof(struct in6_addr)) < 0) {
                return OFPERR_OFPBAC_BAD_ARGUMENT;
            }
        }
    } else if (range_present & NX_NAT_RANGE_IPV6_MAX) {
        return OFPERR_OFPBAC_BAD_ARGUMENT;
    }

    if (range_present & NX_NAT_RANGE_PROTO_MIN) {
        ovs_be16 proto;

        if (nat->range_af == AF_UNSPEC) {
            return OFPERR_OFPBAC_BAD_ARGUMENT;
        }
        if (!NX_NAT_GET_OPT(&proto, opts, len, ovs_be16) || proto == 0) {
            return OFPERR_OFPBAC_BAD_ARGUMENT;
        }
        nat->range.proto.min = ntohs(proto);
        if (range_present & NX_NAT_RANGE_PROTO_MAX) {
            if (!NX_NAT_GET_OPT(&proto, opts, len, ovs_be16)) {
                return OFPERR_OFPBAC_BAD_ARGUMENT;
            }
            nat->range.proto.max = ntohs(proto);
            if (nat->range.proto.max < nat->range.proto.min) {
                return OFPERR_OFPBAC_BAD_ARGUMENT;
            }
        }
    } else if (range_present & NX_NAT_RANGE_PROTO_MAX) {
        return OFPERR_OFPBAC_BAD_ARGUMENT;
    }

    return 0;
}


format_SET_L4_SRC_PORT(const struct ofpact_l4_port *a,
                       const struct ofpact_format_params *fp)
{
    ds_put_format(fp->s, "%smod_tp_src:%s%d",
                  colors.param, colors.end, a->port);
}


parse_NOTE(const char *arg, const struct ofpact_parse_params *pp)
{
    size_t start_ofs = pp->ofpacts->size;
    ofpact_put_NOTE(pp->ofpacts);
    arg = ofpbuf_put_hex(pp->ofpacts, arg, NULL);
    if (arg[0]) {
        return xstrdup("bad hex digit in `note' argument");
    }
    struct ofpact_note *note = ofpbuf_at_assert(pp->ofpacts, start_ofs,
                                                sizeof *note);
    note->length = pp->ofpacts->size - (start_ofs + sizeof *note);
    ofpact_finish_NOTE(pp->ofpacts, &note);
    return NULL;
}


get_subfield(int n_bits, const void **p, struct mf_subfield *sf,
             const struct vl_mff_map *vl_mff_map, uint64_t *tlv_bitmap)
{
    enum ofperr error;

    error = mf_vl_mff_mf_from_nxm_header(ntohl(get_be32(p)), vl_mff_map,
                                         &sf->field, tlv_bitmap);
    sf->ofs = ntohs(get_be16(p));
    sf->n_bits = n_bits;
    return error;
}


check_LEARN(const struct ofpact_learn *a,
            const struct ofpact_check_params *cp)
{
    return learn_check(a, cp->match);
}


decode_NXAST_RAW_SAMPLE2(const struct nx_action_sample2 *nas,
                         enum ofp_version ofp_version OVS_UNUSED,
                         struct ofpbuf *out)
{
    return decode_SAMPLE2(nas, NXAST_RAW_SAMPLE2, NX_ACTION_SAMPLE_DEFAULT,
                          ofpact_put_SAMPLE(out));
}


decode_OFPAT_RAW_SET_MPLS_TTL(uint8_t ttl,
                              enum ofp_version ofp_version OVS_UNUSED,
                              struct ofpbuf *out)
{
    ofpact_put_SET_MPLS_TTL(out)->ttl = ttl;
    return 0;
}


ofpact_name(enum ofpact_type type)
{
    switch (type) {
#define OFPACT(ENUM, STRUCT, MEMBER, NAME) case OFPACT_##ENUM: return NAME;
        OFPACTS
#undef OFPACT
    }
    return "<unknown>";
}


ofpact_finish(struct ofpbuf *ofpacts, struct ofpact *ofpact)
{
    ptrdiff_t len;

    ovs_assert(ofpact == ofpacts->header);
    len = (char *) ofpbuf_tail(ofpacts) - (char *) ofpact;
    ovs_assert(len > 0 && len <= UINT16_MAX);
    ofpact->len = len;
    ofpbuf_padto(ofpacts, OFPACT_ALIGN(ofpacts->size));

    return ofpacts->header;
}


encode_GROUP(const struct ofpact_group *group,
             enum ofp_version ofp_version, struct ofpbuf *out)
{
    put_OFPAT_GROUP(out, ofp_version, group->group_id);
}


encode_POP_MPLS(const struct ofpact_pop_mpls *pop_mpls,
                enum ofp_version ofp_version, struct ofpbuf *out)
{
    put_OFPAT_POP_MPLS(out, ofp_version, pop_mpls->ethertype);
}


parse_OUTPUT(const char *arg, const struct ofpact_parse_params *pp)
{
    if (strstr(arg, "port") && strstr(arg, "max_len")) {
        struct ofpact_output_trunc *output_trunc;

        output_trunc = ofpact_put_OUTPUT_TRUNC(pp->ofpacts);
        return parse_truncate_subfield(arg, pp, output_trunc);
    }

    ofp_port_t port;
    if (ofputil_port_from_string(arg, pp->port_map, &port)) {
        struct ofpact_output *output = ofpact_put_OUTPUT(pp->ofpacts);
        output->port = port;
        output->max_len = output->port == OFPP_CONTROLLER ? UINT16_MAX : 0;
        return NULL;
    }

    struct mf_subfield src;
    char *error = mf_parse_subfield(&src, arg);
    if (!error) {
        struct ofpact_output_reg *output_reg;

        output_reg = ofpact_put_OUTPUT_REG(pp->ofpacts);
        output_reg->max_len = UINT16_MAX;
        output_reg->src = src;
        return NULL;
    }
    free(error);

    return xasprintf("%s: output to unknown port", arg);
}


decode_ONFACT_RAW13_COPY_FIELD(const struct onf_action_copy_field *oacf,
                               enum ofp_version ofp_version OVS_UNUSED,
                               const struct vl_mff_map *vl_mff_map,
                               uint64_t *tlv_bitmap, struct ofpbuf *ofpacts)
{
    return decode_copy_field__(oacf->src_offset, oacf->dst_offset,
                               oacf->n_bits, oacf, oacf->len,
                               OBJECT_OFFSETOF(oacf, pad3), vl_mff_map,
                               tlv_bitmap, ofpacts);
}


check_OUTPUT_REG(const struct ofpact_output_reg *a,
                 const struct ofpact_check_params *cp)
{
    return mf_check_src(&a->src, cp->match);
}


check_POP_QUEUE(const struct ofpact_null *a OVS_UNUSED,
                const struct ofpact_check_params *cp OVS_UNUSED)
{
    return 0;
}


check_POP_MPLS(const struct ofpact_pop_mpls *a, struct ofpact_check_params *cp)
{
    struct flow *flow = &cp->match->flow;
    ovs_be16 dl_type = get_dl_type(flow);

    if (flow->packet_type != htonl(PT_ETH) || !eth_type_mpls(dl_type)) {
        inconsistent_match(&cp->usable_protocols);
    }
    flow->dl_type = a->ethertype;
    return 0;
}


check_RESUBMIT(const struct ofpact_resubmit *a,
               const struct ofpact_check_params *cp)
{
    if (a->with_ct_orig && !is_ct_valid(&cp->match->flow, &cp->match->wc,
                                        NULL)) {
        return OFPERR_OFPBAC_MATCH_INCONSISTENT;
    }
    return 0;
}


parse_DEC_MPLS_TTL(char *arg OVS_UNUSED, const struct ofpact_parse_params *pp)
{
    ofpact_put_DEC_MPLS_TTL(pp->ofpacts);
    return NULL;
}


format_UNROLL_XLATE(const struct ofpact_unroll_xlate *a,
                    const struct ofpact_format_params *fp)
{
    ds_put_format(fp->s, "%sunroll_xlate(%s%stable=%s",
                  colors.paren,   colors.end,
                  colors.special, colors.end);
    ofputil_format_table(a->rule_table_id, fp->table_map, fp->s);
    ds_put_format(fp->s, ", %scookie=%s%"PRIu64"%s)%s",
                  colors.param,   colors.end, ntohll(a->rule_cookie),
                  colors.paren,   colors.end);
}


check_STACK_POP(const struct ofpact_stack *a,
                const struct ofpact_check_params *cp)
{
    return nxm_stack_pop_check(a, cp->match);
}


parse_CLONE(char *arg, const struct ofpact_parse_params *pp)
{
    const size_t clone_offset = ofpacts_pull(pp->ofpacts);
    struct ofpact_nest *clone = ofpact_put_CLONE(pp->ofpacts);
    char *error;

    ofpbuf_pull(pp->ofpacts, sizeof *clone);
    error = ofpacts_parse_copy(arg, pp, false, 0);
    /* header points to the action list */
    pp->ofpacts->header = ofpbuf_push_uninit(pp->ofpacts, sizeof *clone);
    clone = pp->ofpacts->header;

    ofpact_finish_CLONE(pp->ofpacts, &clone);
    ofpbuf_push_uninit(pp->ofpacts, clone_offset);
    return error;
}


parse_METER(char *arg, const struct ofpact_parse_params *pp)
{
    *pp->usable_protocols &= OFPUTIL_P_OF13_UP;
    return str_to_u32(arg, &ofpact_put_METER(pp->ofpacts)->meter_id);
}


parse_WRITE_METADATA(char *arg, const struct ofpact_parse_params *pp)
{
    struct ofpact_metadata *om;
    char *mask = strchr(arg, '/');

    *pp->usable_protocols &= OFPUTIL_P_NXM_OF11_UP;

    om = ofpact_put_WRITE_METADATA(pp->ofpacts);
    if (mask) {
        char *error;

        *mask = '\0';
        error = str_to_be64(mask + 1, &om->mask);
        if (error) {
            return error;
        }
    } else {
        om->mask = OVS_BE64_MAX;
    }

    return str_to_be64(arg, &om->metadata);
}


format_ENQUEUE(const struct ofpact_enqueue *a,
               const struct ofpact_format_params *fp)
{
    ds_put_format(fp->s, "%senqueue:%s", colors.param, colors.end);
    ofputil_format_port(a->port, fp->port_map, fp->s);
    ds_put_format(fp->s, ":%"PRIu32, a->queue);
}


decode_stack_action(const struct nx_action_stack *nasp,
                    const struct vl_mff_map *vl_mff_map, uint64_t *tlv_bitmap,
                    struct ofpact_stack *stack_action)
{
    enum ofperr error;
    stack_action->subfield.ofs = ntohs(nasp->offset);

    struct ofpbuf b = ofpbuf_const_initializer(nasp, sizeof *nasp);
    ofpbuf_pull(&b, OBJECT_OFFSETOF(nasp, pad));
    error  = mf_vl_mff_nx_pull_header(&b, vl_mff_map,
                                      &stack_action->subfield.field, NULL,
                                      tlv_bitmap);
    if (error) {
        return error;
    }

    stack_action->subfield.n_bits = ntohs(*(const ovs_be16 *) b.data);
    ofpbuf_pull(&b, 2);
    if (!is_all_zeros(b.data, b.size)) {
        return OFPERR_NXBRC_MUST_BE_ZERO;
    }

    return 0;
}


check_SAMPLE(const struct ofpact_sample *a OVS_UNUSED,
             const struct ofpact_check_params *cp OVS_UNUSED)
{
    return 0;
}


ofpacts_parse_instructions(const char *s, const struct ofpact_parse_params *pp)
{
    return ofpacts_parse_copy(s, pp, true, 0);
}


decode_OFPAT_RAW11_SET_VLAN_VID(uint16_t vid,
                                enum ofp_version ofp_version OVS_UNUSED,
                                struct ofpbuf *out)
{
    return decode_set_vlan_vid(vid, false, out);
}


format_SET_IP_ECN(const struct ofpact_ecn *a,
                  const struct ofpact_format_params *fp)
{
    ds_put_format(fp->s, "%smod_nw_ecn:%s%d",
                  colors.param, colors.end, a->ecn);
}


decode_OFPAT_RAW_SET_TP_SRC(ovs_be16 port,
                            enum ofp_version ofp_version OVS_UNUSED,
                            struct ofpbuf *out)
{
    ofpact_put_SET_L4_SRC_PORT(out)->port = ntohs(port);
    return 0;
}


format_MULTIPATH(const struct ofpact_multipath *a,
                 const struct ofpact_format_params *fp)
{
    multipath_format(a, fp->s);
}


decode_ct_zone(const struct nx_action_conntrack *nac,
               struct ofpact_conntrack *out,
               const struct vl_mff_map *vl_mff_map, uint64_t *tlv_bitmap)
{
    if (nac->zone_src) {
        enum ofperr error;

        out->zone_src.ofs = nxm_decode_ofs(nac->zone_ofs_nbits);
        out->zone_src.n_bits = nxm_decode_n_bits(nac->zone_ofs_nbits);
        error = mf_vl_mff_mf_from_nxm_header(ntohl(nac->zone_src),
                                             vl_mff_map, &out->zone_src.field,
                                             tlv_bitmap);
        if (error) {
            return error;
        }

        error = mf_check_src(&out->zone_src, NULL);
        if (error) {
            return error;
        }

        if (out->zone_src.n_bits != 16) {
            VLOG_WARN_RL(&rl, "zone n_bits %d not within valid range [16..16]",
                         out->zone_src.n_bits);
            return OFPERR_OFPBAC_BAD_SET_LEN;
        }
    } else {
        out->zone_src.field = NULL;
        out->zone_imm = ntohs(nac->zone_imm);
    }

    return 0;
}


ofpact_check__(struct ofpact *a, struct ofpact_check_params *cp)
{
    switch (a->type) {
#define OFPACT(ENUM, STRUCT, MEMBER, NAME)                  \
        case OFPACT_##ENUM:                                 \
            return check_##ENUM(ofpact_get_##ENUM(a), cp);
        OFPACTS
#undef OFPACT
    default:
        OVS_NOT_REACHED();
    }
}


format_SET_VLAN_PCP(const struct ofpact_vlan_pcp *a,
                    const struct ofpact_format_params *fp)
{
    ds_put_format(fp->s, "%s%s:%s%"PRIu8, colors.param,
                  a->push_vlan_if_needed ? "mod_vlan_pcp" : "set_vlan_pcp",
                  colors.end, a->vlan_pcp);
}


format_SET_FIELD(const struct ofpact_set_field *a,
                 const struct ofpact_format_params *fp)
{
    if (a->ofpact.raw == NXAST_RAW_REG_LOAD) {
        struct mf_subfield dst;
        uint64_t value;

        dst.ofs = dst.n_bits = 0;
        while (next_load_segment(a, &dst, &value)) {
            ds_put_format(fp->s, "%sload:%s%#"PRIx64"%s->%s",
                          colors.special, colors.end, value,
                          colors.special, colors.end);
            mf_format_subfield(&dst, fp->s);
            ds_put_char(fp->s, ',');
        }
        ds_chomp(fp->s, ',');
    } else {
        ds_put_format(fp->s, "%sset_field:%s", colors.special, colors.end);
        mf_format(a->field, a->value, ofpact_set_field_mask(a),
                  fp->port_map, fp->s);
        ds_put_format(fp->s, "%s->%s%s",
                      colors.special, colors.end, a->field->name);
    }
}


encode_SET_QUEUE(const struct ofpact_queue *queue,
                 enum ofp_version ofp_version, struct ofpbuf *out)
{
    put_OFPAT_SET_QUEUE(out, ofp_version, queue->queue_id);
}


check_EXIT(const struct ofpact_null *a OVS_UNUSED,
           const struct ofpact_check_params *cp OVS_UNUSED)
{
    return 0;
}


check_set_l4_port(struct ofpact_l4_port *a, struct ofpact_check_params *cp)
{
    const struct flow *flow = &cp->match->flow;
    if (!is_ip_any(flow)
        || flow->nw_frag & FLOW_NW_FRAG_LATER
        || (flow->nw_proto != IPPROTO_TCP &&
            flow->nw_proto != IPPROTO_UDP &&
            flow->nw_proto != IPPROTO_SCTP)) {
        inconsistent_match(&cp->usable_protocols);
    }

    /* Note the transport protocol in use, to allow this action to be converted
     * to an OF1.2 set_field action later if necessary. */
    a->flow_ip_proto = flow->nw_proto;

    return 0;
}


parse_SET_IPV4_DST(char *arg, const struct ofpact_parse_params *pp)
{
    return str_to_ip(arg, &ofpact_put_SET_IPV4_DST(pp->ofpacts)->ipv4);
}


decode_OFPAT_RAW15_SET_FIELD(const struct ofp12_action_set_field *oasf,
                             enum ofp_version ofp_version OVS_UNUSED,
                             const struct vl_mff_map *vl_mff_map,
                             uint64_t *tlv_bitmap, struct ofpbuf *ofpacts)
{
    return decode_ofpat_set_field(oasf, true, vl_mff_map, tlv_bitmap, ofpacts);
}


decode_NXAST_RAW_REG_MOVE(const struct nx_action_reg_move *narm,
                          enum ofp_version ofp_version OVS_UNUSED,
                          const struct vl_mff_map *vl_mff_map,
                          uint64_t *tlv_bitmap, struct ofpbuf *ofpacts)
{
    struct ofpact_reg_move *move = ofpact_put_REG_MOVE(ofpacts);
    enum ofperr error;

    move->ofpact.raw = NXAST_RAW_REG_MOVE;
    move->src.ofs = ntohs(narm->src_ofs);
    move->src.n_bits = ntohs(narm->n_bits);
    move->dst.ofs = ntohs(narm->dst_ofs);
    move->dst.n_bits = ntohs(narm->n_bits);

    struct ofpbuf b = ofpbuf_const_initializer(narm, ntohs(narm->len));
    ofpbuf_pull(&b, sizeof *narm);

    error = mf_vl_mff_nx_pull_header(&b, vl_mff_map, &move->src.field, NULL,
                                     tlv_bitmap);
    if (error) {
        return error;
    }

    error = mf_vl_mff_nx_pull_header(&b, vl_mff_map, &move->dst.field, NULL,
                                     tlv_bitmap);
    if (error) {
        return error;
    }

    if (!is_all_zeros(b.data, b.size)) {
        return OFPERR_NXBRC_MUST_BE_ZERO;
    }

    return nxm_reg_move_check(move, NULL);
}


encode_SET_L4_SRC_PORT(const struct ofpact_l4_port *l4_port,
                       enum ofp_version ofp_version, struct ofpbuf *out)
{
    uint8_t proto = l4_port->flow_ip_proto;
    enum mf_field_id field = (proto == IPPROTO_TCP ? MFF_TCP_SRC
                              : proto == IPPROTO_UDP ? MFF_UDP_SRC
                              : proto == IPPROTO_SCTP ? MFF_SCTP_SRC
                              : MFF_N_IDS);

    encode_SET_L4_port(l4_port, ofp_version, OFPAT_RAW_SET_TP_SRC, field, out);
}


format_REG_MOVE(const struct ofpact_reg_move *a,
                const struct ofpact_format_params *fp)
{
    nxm_format_reg_move(a, fp->s);
}


format_alg(int port, struct ds *s)
{
    switch(port) {
    case IPPORT_FTP:
        ds_put_format(s, "%salg=%sftp,", colors.param, colors.end);
        break;
    case IPPORT_TFTP:
        ds_put_format(s, "%salg=%stftp,", colors.param, colors.end);
        break;
    case 0:
        /* Don't print. */
        break;
    default:
        ds_put_format(s, "%salg=%s%d,", colors.param, colors.end, port);
        break;
    }
}


check_set_ipv4(struct ofpact_check_params *cp)
{
    ovs_be16 dl_type = get_dl_type(&cp->match->flow);
    if (dl_type != htons(ETH_TYPE_IP)) {
        inconsistent_match(&cp->usable_protocols);
    }
    return 0;
}


check_ENCAP(const struct ofpact_encap *a, struct ofpact_check_params *cp)
{
    struct flow *flow = &cp->match->flow;
    flow->packet_type = a->new_pkt_type;
    if (pt_ns(flow->packet_type) == OFPHTN_ETHERTYPE) {
        flow->dl_type = htons(pt_ns_type(flow->packet_type));
    }
    if (!is_ip_any(flow)) {
        flow->nw_proto = 0;
    }
    return 0;
}


encode_METER(const struct ofpact_meter *meter,
             enum ofp_version ofp_version, struct ofpbuf *out)
{
    if (ofp_version >= OFP13_VERSION) {
        instruction_put_OFPIT13_METER(out)->meter_id = htonl(meter->meter_id);
    }
}


set_field_to_set_field(const struct ofpact_set_field *sf,
                       enum ofp_version ofp_version, struct ofpbuf *out)
{
    struct ofp12_action_set_field *oasf OVS_UNUSED;
    size_t start_ofs = out->size;

    oasf = put_OFPAT12_SET_FIELD(out);
    out->size = out->size - sizeof oasf->pad;
    nx_put_entry(out, sf->field, ofp_version, sf->value,
                 ofpact_set_field_mask(sf));
    pad_ofpat(out, start_ofs);
}


parse_DECAP(char *arg, const struct ofpact_parse_params *pp)
{
    struct ofpact_decap *decap;
    char *key, *value, *pos;
    char *error = NULL;
    uint16_t ns, type;

    decap = ofpact_put_DECAP(pp->ofpacts);
    /* Default next packet_type is PT_USE_NEXT_PROTO. */
    decap->new_pkt_type = htonl(PT_USE_NEXT_PROTO);

    /* Parse decap packet_type if given. */
    if (ofputil_parse_key_value(&arg, &key, &value)) {
        if (strcmp(key, "packet_type") == 0) {
            pos = value;
            if (!ofputil_parse_key_value(&pos, &key, &value)
                || strcmp(key, "ns") != 0) {
                return xstrdup("Missing packet_type attribute ns");
            }
            error = str_to_u16(value, "ns", &ns);
            if (error) {
                return error;
            }
            if (ns >= OFPHTN_N_TYPES) {
                return xasprintf("Unsupported ns value: %"PRIu16, ns);
            }
            if (!ofputil_parse_key_value(&pos, &key, &value)
                || strcmp(key, "type") != 0) {
                return xstrdup("Missing packet_type attribute type");
            }
            error = str_to_u16(value, "type", &type);
            if (error) {
                return error;
            }
            decap->new_pkt_type = htonl(PACKET_TYPE(ns, type));
        } else {
            return xasprintf("Invalid decap argument: %s", key);
        }
    }
    return NULL;
}


get_actions_from_instruction(const struct ofp11_instruction *inst,
                             const struct ofp_action_header **actions,
                             size_t *actions_len)
{
    *actions = ALIGNED_CAST(const struct ofp_action_header *, inst + 1);
    *actions_len = ntohs(inst->len) - sizeof *inst;
}


format_OUTPUT(const struct ofpact_output *a,
              const struct ofpact_format_params *fp)
{
    if (ofp_to_u16(a->port) < ofp_to_u16(OFPP_MAX)) {
        ds_put_format(fp->s, "%soutput:%s", colors.special, colors.end);
    }
    ofputil_format_port(a->port, fp->port_map, fp->s);
    if (a->port == OFPP_CONTROLLER) {
        ds_put_format(fp->s, ":%"PRIu16, a->max_len);
    }
}


parse_DEBUG_SLOW(char *arg OVS_UNUSED, const struct ofpact_parse_params *pp)
{
    ofpact_put_DEBUG_SLOW(pp->ofpacts);
    return NULL;
}


ovs_instruction_type_from_ofpact_type(enum ofpact_type type)
{
    switch (type) {
    case OFPACT_METER:
        return OVSINST_OFPIT13_METER;
    case OFPACT_CLEAR_ACTIONS:
        return OVSINST_OFPIT11_CLEAR_ACTIONS;
    case OFPACT_WRITE_ACTIONS:
        return OVSINST_OFPIT11_WRITE_ACTIONS;
    case OFPACT_WRITE_METADATA:
        return OVSINST_OFPIT11_WRITE_METADATA;
    case OFPACT_GOTO_TABLE:
        return OVSINST_OFPIT11_GOTO_TABLE;
    case OFPACT_OUTPUT:
    case OFPACT_GROUP:
    case OFPACT_CLONE:
    case OFPACT_CONTROLLER:
    case OFPACT_ENQUEUE:
    case OFPACT_OUTPUT_REG:
    case OFPACT_OUTPUT_TRUNC:
    case OFPACT_BUNDLE:
    case OFPACT_SET_VLAN_VID:
    case OFPACT_SET_VLAN_PCP:
    case OFPACT_STRIP_VLAN:
    case OFPACT_PUSH_VLAN:
    case OFPACT_SET_ETH_SRC:
    case OFPACT_SET_ETH_DST:
    case OFPACT_SET_IPV4_SRC:
    case OFPACT_SET_IPV4_DST:
    case OFPACT_SET_IP_DSCP:
    case OFPACT_SET_IP_ECN:
    case OFPACT_SET_IP_TTL:
    case OFPACT_SET_L4_SRC_PORT:
    case OFPACT_SET_L4_DST_PORT:
    case OFPACT_REG_MOVE:
    case OFPACT_SET_FIELD:
    case OFPACT_STACK_PUSH:
    case OFPACT_STACK_POP:
    case OFPACT_DEC_TTL:
    case OFPACT_SET_MPLS_LABEL:
    case OFPACT_SET_MPLS_TC:
    case OFPACT_SET_MPLS_TTL:
    case OFPACT_DEC_MPLS_TTL:
    case OFPACT_PUSH_MPLS:
    case OFPACT_POP_MPLS:
    case OFPACT_SET_TUNNEL:
    case OFPACT_SET_QUEUE:
    case OFPACT_POP_QUEUE:
    case OFPACT_FIN_TIMEOUT:
    case OFPACT_RESUBMIT:
    case OFPACT_LEARN:
    case OFPACT_CONJUNCTION:
    case OFPACT_MULTIPATH:
    case OFPACT_NOTE:
    case OFPACT_EXIT:
    case OFPACT_UNROLL_XLATE:
    case OFPACT_SAMPLE:
    case OFPACT_DEBUG_RECIRC:
    case OFPACT_DEBUG_SLOW:
    case OFPACT_CT:
    case OFPACT_CT_CLEAR:
    case OFPACT_NAT:
    case OFPACT_ENCAP:
    case OFPACT_DECAP:
    case OFPACT_DEC_NSH_TTL:
    default:
        return OVSINST_OFPIT11_APPLY_ACTIONS;
    }
}


parse_OUTPUT_REG(const char *arg, const struct ofpact_parse_params *pp)
{
    return parse_OUTPUT(arg, pp);
}


encode_REG_MOVE(const struct ofpact_reg_move *move,
                enum ofp_version ofp_version, struct ofpbuf *out)
{
    /* For OpenFlow 1.3, the choice of ONFACT_RAW13_COPY_FIELD versus
     * NXAST_RAW_REG_MOVE is somewhat difficult.  Neither one is guaranteed to
     * be supported by every OpenFlow 1.3 implementation.  It would be ideal to
     * probe for support.  Until we have that ability, we currently prefer
     * NXAST_RAW_REG_MOVE for backward compatibility with older Open vSwitch
     * versions.  */
    size_t start_ofs = out->size;
    if (ofp_version >= OFP15_VERSION) {
        struct ofp15_action_copy_field *copy = put_OFPAT15_COPY_FIELD(out);
        copy->n_bits = htons(move->dst.n_bits);
        copy->src_offset = htons(move->src.ofs);
        copy->dst_offset = htons(move->dst.ofs);
        out->size = out->size - sizeof copy->pad2;
        nx_put_mff_header(out, move->src.field, ofp_version, false);
        nx_put_mff_header(out, move->dst.field, ofp_version, false);
    } else if (ofp_version == OFP13_VERSION
               && move->ofpact.raw == ONFACT_RAW13_COPY_FIELD) {
        struct onf_action_copy_field *copy = put_ONFACT13_COPY_FIELD(out);
        copy->n_bits = htons(move->dst.n_bits);
        copy->src_offset = htons(move->src.ofs);
        copy->dst_offset = htons(move->dst.ofs);
        out->size = out->size - sizeof copy->pad3;
        nx_put_mff_header(out, move->src.field, ofp_version, false);
        nx_put_mff_header(out, move->dst.field, ofp_version, false);
    } else {
        struct nx_action_reg_move *narm = put_NXAST_REG_MOVE(out);
        narm->n_bits = htons(move->dst.n_bits);
        narm->src_ofs = htons(move->src.ofs);
        narm->dst_ofs = htons(move->dst.ofs);
        nx_put_mff_header(out, move->src.field, 0, false);
        nx_put_mff_header(out, move->dst.field, 0, false);
    }
    pad_ofpat(out, start_ofs);
}


encode_CONJUNCTION(const struct ofpact_conjunction *oc,
                   enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out)
{
    struct nx_action_conjunction *nac = put_NXAST_CONJUNCTION(out);
    nac->clause = oc->clause;
    nac->n_clauses = oc->n_clauses;
    nac->id = htonl(oc->id);
}


decode_bundle(bool load, const struct nx_action_bundle *nab,
              const struct vl_mff_map *vl_mff_map, uint64_t *tlv_bitmap,
              struct ofpbuf *ofpacts)
{
    static struct vlog_rate_limit rll = VLOG_RATE_LIMIT_INIT(1, 5);
    struct ofpact_bundle *bundle;
    uint32_t slave_type;
    size_t slaves_size, i;
    enum ofperr error;

    bundle = ofpact_put_BUNDLE(ofpacts);

    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(bundle->fields)) {
        VLOG_WARN_RL(&rll, "unsupported fields %d", (int) bundle->fields);
    } else if (bundle->n_slaves > BUNDLE_MAX_SLAVES) {
        VLOG_WARN_RL(&rll, "too many slaves");
    } else if (bundle->algorithm != NX_BD_ALG_HRW
               && bundle->algorithm != NX_BD_ALG_ACTIVE_BACKUP) {
        VLOG_WARN_RL(&rll, "unsupported algorithm %d", (int) bundle->algorithm);
    } else if (slave_type != mf_nxm_header(MFF_IN_PORT)) {
        VLOG_WARN_RL(&rll, "unsupported slave type %"PRIu32, slave_type);
    } else {
        error = 0;
    }

    if (!is_all_zeros(nab->zero, sizeof nab->zero)) {
        VLOG_WARN_RL(&rll, "reserved field is nonzero");
        error = OFPERR_OFPBAC_BAD_ARGUMENT;
    }

    if (load) {
        bundle->dst.ofs = nxm_decode_ofs(nab->ofs_nbits);
        bundle->dst.n_bits = nxm_decode_n_bits(nab->ofs_nbits);
        error = mf_vl_mff_mf_from_nxm_header(ntohl(nab->dst), vl_mff_map,
                                             &bundle->dst.field, tlv_bitmap);
        if (error) {
            return error;
        }

        if (bundle->dst.n_bits < 16) {
            VLOG_WARN_RL(&rll, "bundle_load action requires at least 16 bit "
                         "destination.");
            error = OFPERR_OFPBAC_BAD_ARGUMENT;
        }
    } else {
        if (nab->ofs_nbits || nab->dst) {
            VLOG_WARN_RL(&rll, "bundle action has nonzero reserved fields");
            error = OFPERR_OFPBAC_BAD_ARGUMENT;
        }
    }

    if (slaves_size < bundle->n_slaves * sizeof(ovs_be16)) {
        VLOG_WARN_RL(&rll, "Nicira action %s only has %"PRIuSIZE" bytes "
                     "allocated for slaves.  %"PRIuSIZE" bytes are required "
                     "for %u slaves.",
                     load ? "bundle_load" : "bundle", slaves_size,
                     bundle->n_slaves * sizeof(ovs_be16), bundle->n_slaves);
        error = OFPERR_OFPBAC_BAD_LEN;
    } else {
        for (i = 0; i < bundle->n_slaves; i++) {
            ofp_port_t ofp_port
                = u16_to_ofp(ntohs(((ovs_be16 *)(nab + 1))[i]));
            ofpbuf_put(ofpacts, &ofp_port, sizeof ofp_port);
            bundle = ofpacts->header;
        }
    }

    ofpact_finish_BUNDLE(ofpacts, &bundle);
    if (!error) {
        error = bundle_check(bundle, OFPP_MAX, NULL);
    }
    return error;
}


encode_SET_MPLS_TC(const struct ofpact_mpls_tc *tc,
                   enum ofp_version ofp_version, struct ofpbuf *out)
{
    if (ofp_version < OFP12_VERSION) {
        put_OFPAT_SET_MPLS_TC(out, ofp_version, tc->tc);
    } else {
        put_set_field(out, ofp_version, MFF_MPLS_TC, tc->tc);
    }
}


parse_SET_VLAN_VID(char *arg, const struct ofpact_parse_params *pp)
{
    return parse_set_vlan_vid(arg, false, pp);
}


learn_min_len(uint16_t header)
{
    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;
    unsigned int min_len;

    min_len = 0;
    if (src_type == NX_LEARN_SRC_FIELD) {
        min_len += sizeof(ovs_be32); /* src_field */
        min_len += sizeof(ovs_be16); /* src_ofs */
    } else {
        min_len += 2 * DIV_ROUND_UP(n_bits, 16);
    }
    if (dst_type == NX_LEARN_DST_MATCH ||
        dst_type == NX_LEARN_DST_LOAD) {
        min_len += sizeof(ovs_be32); /* dst_field */
        min_len += sizeof(ovs_be16); /* dst_ofs */
    }
    return min_len;
}


ofpacts_pull_openflow_actions(struct ofpbuf *openflow,
                              unsigned int actions_len,
                              enum ofp_version version,
                              const struct vl_mff_map *vl_mff_map,
                              uint64_t *ofpacts_tlv_bitmap,
                              struct ofpbuf *ofpacts)
{
    return ofpacts_pull_openflow_actions__(openflow, actions_len, version,
                                           1u << OVSINST_OFPIT11_APPLY_ACTIONS,
                                           ofpacts, 0, vl_mff_map,
                                           ofpacts_tlv_bitmap);
}


ofpacts_parse__(char *str, const struct ofpact_parse_params *pp,
                bool allow_instructions, enum ofpact_type outer_action)
{
    int prev_inst = -1;
    enum ofperr retval;
    char *key, *value;
    bool drop = false;
    char *pos;

    pos = str;
    while (ofputil_parse_key_value(&pos, &key, &value)) {
        enum ovs_instruction_type inst = OVSINST_OFPIT11_APPLY_ACTIONS;
        enum ofpact_type type;
        char *error = NULL;
        ofp_port_t port;

        if (ofpact_type_from_name(key, &type)) {
            error = ofpact_parse(type, value, pp);
            inst = ovs_instruction_type_from_ofpact_type(type);
        } else if (!strcasecmp(key, "mod_vlan_vid")) {
            error = parse_set_vlan_vid(value, true, pp);
        } else if (!strcasecmp(key, "mod_vlan_pcp")) {
            error = parse_set_vlan_pcp(value, true, pp);
        } else if (!strcasecmp(key, "set_nw_ttl")) {
            error = parse_SET_IP_TTL(value, pp);
        } else if (!strcasecmp(key, "pop_vlan")) {
            error = parse_pop_vlan(pp);
        } else if (!strcasecmp(key, "set_tunnel64")) {
            error = parse_set_tunnel(value, NXAST_RAW_SET_TUNNEL64, pp);
        } else if (!strcasecmp(key, "load")) {
            error = parse_reg_load(value, pp);
        } else if (!strcasecmp(key, "bundle_load")) {
            error = parse_bundle_load(value, pp);
        } else if (!strcasecmp(key, "drop")) {
            drop = true;
        } else if (!strcasecmp(key, "apply_actions")) {
            return xstrdup("apply_actions is the default instruction");
        } else if (ofputil_port_from_string(key, pp->port_map, &port)) {
            ofpact_put_OUTPUT(pp->ofpacts)->port = port;
        } else {
            return xasprintf("unknown action %s", key);
        }
        if (error) {
            return error;
        }

        if (inst != OVSINST_OFPIT11_APPLY_ACTIONS) {
            if (!allow_instructions) {
                return xasprintf("only actions are allowed here (not "
                                 "instruction %s)",
                                 ovs_instruction_name_from_type(inst));
            }
            if (inst == prev_inst) {
                return xasprintf("instruction %s may be specified only once",
                                 ovs_instruction_name_from_type(inst));
            }
        }
        if (prev_inst != -1 && inst < prev_inst) {
            return xasprintf("instruction %s must be specified before %s",
                             ovs_instruction_name_from_type(inst),
                             ovs_instruction_name_from_type(prev_inst));
        }
        prev_inst = inst;
    }

    if (drop && pp->ofpacts->size) {
        return xstrdup("\"drop\" must not be accompanied by any other action "
                       "or instruction");
    }

    retval = ofpacts_verify(pp->ofpacts->data, pp->ofpacts->size,
                            (allow_instructions
                             ? (1u << N_OVS_INSTRUCTIONS) - 1
                             : 1u << OVSINST_OFPIT11_APPLY_ACTIONS),
                            outer_action);
    if (retval) {
        return xstrdup("Incorrect instruction ordering");
    }

    return NULL;
}


parse_FIN_TIMEOUT(char *arg, const struct ofpact_parse_params *pp)
{
    struct ofpact_fin_timeout *oft = ofpact_put_FIN_TIMEOUT(pp->ofpacts);
    char *key, *value;

    while (ofputil_parse_key_value(&arg, &key, &value)) {
        char *error;

        if (!strcmp(key, "idle_timeout")) {
            error =  str_to_u16(value, key, &oft->fin_idle_timeout);
        } else if (!strcmp(key, "hard_timeout")) {
            error = str_to_u16(value, key, &oft->fin_hard_timeout);
        } else {
            error = xasprintf("invalid key '%s' in 'fin_timeout' argument",
                              key);
        }

        if (error) {
            return error;
        }
    }
    return NULL;
}


parse_DEC_TTL(char *arg, const struct ofpact_parse_params *pp)
{
    if (*arg == '\0') {
        parse_noargs_dec_ttl(pp);
    } else {
        struct ofpact_cnt_ids *ids;
        char *cntr;

        ids = ofpact_put_DEC_TTL(pp->ofpacts);
        ids->ofpact.raw = NXAST_RAW_DEC_TTL_CNT_IDS;
        for (cntr = strtok_r(arg, ", ", &arg); cntr != NULL;
             cntr = strtok_r(NULL, ", ", &arg)) {
            uint16_t id = atoi(cntr);

            ofpbuf_put(pp->ofpacts, &id, sizeof id);
            ids = pp->ofpacts->header;
            ids->n_controllers++;
        }
        if (!ids->n_controllers) {
            return xstrdup("dec_ttl_cnt_ids: expected at least one controller "
                           "id.");
        }
        ofpact_finish_DEC_TTL(pp->ofpacts, &ids);
    }
    return NULL;
}


decode_OFPAT_RAW_PUSH_MPLS(ovs_be16 ethertype,
                           enum ofp_version ofp_version OVS_UNUSED,
                           struct ofpbuf *out)
{
    struct ofpact_push_mpls *oam;

    if (!eth_type_mpls(ethertype)) {
        return OFPERR_OFPBAC_BAD_ARGUMENT;
    }
    oam = ofpact_put_PUSH_MPLS(out);
    oam->ethertype = ethertype;

    return 0;
}


check_METER(const struct ofpact_meter *a,
            const struct ofpact_check_params *cp OVS_UNUSED)
{
    uint32_t mid = a->meter_id;
    return mid == 0 || mid > OFPM13_MAX ? OFPERR_OFPMMFC_INVALID_METER : 0;
}


encode_CONTROLLER(const struct ofpact_controller *controller,
                  enum ofp_version ofp_version OVS_UNUSED,
                  struct ofpbuf *out)
{
    if (controller->userdata_len
        || controller->pause
        || controller->meter_id != NX_CTLR_NO_METER
        || controller->ofpact.raw == NXAST_RAW_CONTROLLER2) {
        size_t start_ofs = out->size;
        put_NXAST_CONTROLLER2(out);
        if (controller->max_len != UINT16_MAX) {
            ofpprop_put_u16(out, NXAC2PT_MAX_LEN, controller->max_len);
        }
        if (controller->controller_id != 0) {
            ofpprop_put_u16(out, NXAC2PT_CONTROLLER_ID,
                            controller->controller_id);
        }
        if (controller->reason != OFPR_ACTION) {
            ofpprop_put_u8(out, NXAC2PT_REASON, controller->reason);
        }
        if (controller->userdata_len != 0) {
            ofpprop_put(out, NXAC2PT_USERDATA, controller->userdata,
                        controller->userdata_len);
        }
        if (controller->pause) {
            ofpprop_put_flag(out, NXAC2PT_PAUSE);
        }
        if (controller->meter_id != NX_CTLR_NO_METER) {
            ofpprop_put_u32(out, NXAC2PT_METER_ID, controller->meter_id);
        }
        pad_ofpat(out, start_ofs);
    } else {
        struct nx_action_controller *nac;

        nac = put_NXAST_CONTROLLER(out);
        nac->max_len = htons(controller->max_len);
        nac->controller_id = htons(controller->controller_id);
        nac->reason = controller->reason;
    }
}


format_DEBUG_SLOW(const struct ofpact_null *a OVS_UNUSED,
                  const struct ofpact_format_params *fp)
{
    ds_put_format(fp->s, "%sdebug_slow%s", colors.value, colors.end);
}


decode_NXAST_RAW_MULTIPATH(const struct nx_action_multipath *nam,
                           enum ofp_version ofp_version OVS_UNUSED,
                           const struct vl_mff_map *vl_mff_map,
                           uint64_t *tlv_bitmap, struct ofpbuf *out)
{
    uint32_t n_links = ntohs(nam->max_link) + 1;
    size_t min_n_bits = log_2_ceil(n_links);
    struct ofpact_multipath *mp;
    enum ofperr error;

    mp = ofpact_put_MULTIPATH(out);
    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.ofs = nxm_decode_ofs(nam->ofs_nbits);
    mp->dst.n_bits = nxm_decode_n_bits(nam->ofs_nbits);
    error = mf_vl_mff_mf_from_nxm_header(ntohl(nam->dst), vl_mff_map,
                                         &mp->dst.field, tlv_bitmap);
    if (error) {
        return error;
    }

    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 %"PRIuSIZE" bits for "
                     "%"PRIu32" links", min_n_bits, n_links);
        return OFPERR_OFPBAC_BAD_ARGUMENT;
    }

    return multipath_check(mp, NULL);
}


format_SET_ETH_DST(const struct ofpact_mac *a,
                   const struct ofpact_format_params *fp)
{
    ds_put_format(fp->s, "%smod_dl_dst:%s"ETH_ADDR_FMT,
                  colors.param, colors.end, ETH_ADDR_ARGS(a->mac));
}


parse_SET_QUEUE(char *arg, const struct ofpact_parse_params *pp)
{
    return str_to_u32(arg, &ofpact_put_SET_QUEUE(pp->ofpacts)->queue_id);
}


pad_ofpat(struct ofpbuf *openflow, size_t start_ofs)
{
    struct ofp_action_header *oah;

    ofpbuf_put_zeros(openflow, PAD_SIZE(openflow->size - start_ofs,
                                        OFP_ACTION_ALIGN));

    oah = ofpbuf_at_assert(openflow, start_ofs, sizeof *oah);
    oah->len = htons(openflow->size - start_ofs);
}


decode_NXAST_RAW_CT_CLEAR(struct ofpbuf *out)
{
    ofpact_put_CT_CLEAR(out);
    return 0;
}


parse_set_vlan_vid(char *arg, bool push_vlan_if_needed,
                   const struct ofpact_parse_params *pp)
{
    struct ofpact_vlan_vid *vlan_vid;
    uint16_t vid;
    char *error;

    error = str_to_u16(arg, "VLAN VID", &vid);
    if (error) {
        return error;
    }

    if (vid & ~VLAN_VID_MASK) {
        return xasprintf("%s: not a valid VLAN VID", arg);
    }
    vlan_vid = ofpact_put_SET_VLAN_VID(pp->ofpacts);
    vlan_vid->vlan_vid = vid;
    vlan_vid->push_vlan_if_needed = push_vlan_if_needed;
    return NULL;
}


put_be16(struct ofpbuf *b, ovs_be16 x)
{
    ofpbuf_put(b, &x, sizeof x);
}


encode_SET_ETH_SRC(const struct ofpact_mac *mac, enum ofp_version ofp_version,
                   struct ofpbuf *out)
{
    encode_SET_ETH_addr(mac, ofp_version, OFPAT_RAW_SET_DL_SRC, MFF_ETH_SRC,
                        out);

}


format_STRIP_VLAN(const struct ofpact_null *a,
                  const struct ofpact_format_params *fp)
{
    ds_put_format(fp->s, (a->ofpact.raw == OFPAT_RAW11_POP_VLAN
                    ? "%spop_vlan%s"
                    : "%sstrip_vlan%s"),
                  colors.value, colors.end);
}


encode_DECAP(const struct ofpact_decap *decap,
                enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out)
{
    struct nx_action_decap *nad = put_NXAST_DECAP(out);

    nad->len = htons(sizeof(struct nx_action_decap));
    nad->new_pkt_type = decap->new_pkt_type;
}


check_OUTPUT_TRUNC(const struct ofpact_output_trunc *a,
                   const struct ofpact_check_params *cp)
{
    return ofpact_check_output_port(a->port, cp->max_ports);
}


encode_UNROLL_XLATE(const struct ofpact_unroll_xlate *unroll OVS_UNUSED,
                    enum ofp_version ofp_version OVS_UNUSED,
                    struct ofpbuf *out OVS_UNUSED)
{
    OVS_NOT_REACHED();
}


decode_NXAST_RAW_WRITE_METADATA(const struct nx_action_write_metadata *nawm,
                                enum ofp_version ofp_version OVS_UNUSED,
                                struct ofpbuf *out)
{
    struct ofpact_metadata *om;

    if (!is_all_zeros(nawm->zeros, sizeof nawm->zeros)) {
        return OFPERR_NXBRC_MUST_BE_ZERO;
    }

    om = ofpact_put_WRITE_METADATA(out);
    om->metadata = nawm->metadata;
    om->mask = nawm->mask;

    return 0;
}


encode_SET_ETH_DST(const struct ofpact_mac *mac,
                               enum ofp_version ofp_version,
                               struct ofpbuf *out)
{
    encode_SET_ETH_addr(mac, ofp_version, OFPAT_RAW_SET_DL_DST, MFF_ETH_DST,
                        out);

}


decode_NXAST_RAW_SAMPLE3(const struct nx_action_sample2 *nas,
                         enum ofp_version ofp_version OVS_UNUSED,
                         struct ofpbuf *out)
{
    struct ofpact_sample *sample = ofpact_put_SAMPLE(out);
    if (!is_all_zeros(nas->zeros, sizeof nas->zeros)) {
        return OFPERR_NXBRC_MUST_BE_ZERO;
    }
    if (nas->direction != NX_ACTION_SAMPLE_DEFAULT &&
        nas->direction != NX_ACTION_SAMPLE_INGRESS &&
        nas->direction != NX_ACTION_SAMPLE_EGRESS) {
        VLOG_WARN_RL(&rl, "invalid sample direction %"PRIu8, nas->direction);
        return OFPERR_OFPBAC_BAD_ARGUMENT;
    }
    return decode_SAMPLE2(nas, NXAST_RAW_SAMPLE3, nas->direction, sample);
}


ofpact_bitmap_format(uint64_t ofpacts_bitmap, struct ds *s)
{
    if (!ofpacts_bitmap) {
        ds_put_cstr(s, "<none>");
    } else {
        while (ofpacts_bitmap) {
            ds_put_format(s, "%s ",
                          ofpact_name(rightmost_1bit_idx(ofpacts_bitmap)));
            ofpacts_bitmap = zero_rightmost_1bit(ofpacts_bitmap);
        }
        ds_chomp(s, ' ');
    }
}


encode_SET_VLAN_PCP(const struct ofpact_vlan_pcp *vlan_pcp,
                    enum ofp_version ofp_version, struct ofpbuf *out)
{
    uint8_t pcp = vlan_pcp->vlan_pcp;

    /* Push a VLAN tag, if none is present and this form of the action calls
     * for such a feature. */
    if (ofp_version > OFP10_VERSION
        && vlan_pcp->push_vlan_if_needed
        && !vlan_pcp->flow_has_vlan) {
        put_OFPAT11_PUSH_VLAN(out, htons(ETH_TYPE_VLAN_8021Q));
    }

    if (ofp_version == OFP10_VERSION) {
        put_OFPAT10_SET_VLAN_PCP(out, pcp);
    } else if (ofp_version == OFP11_VERSION) {
        put_OFPAT11_SET_VLAN_PCP(out, pcp);
    } else {
        put_set_field(out, ofp_version, MFF_VLAN_PCP, pcp);
    }
}


inconsistent_match(enum ofputil_protocol *usable_protocols)
{
    *usable_protocols &= OFPUTIL_P_OF10_ANY;
}


encode_POP_QUEUE(const struct ofpact_null *null OVS_UNUSED,
                 enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out)
{
    put_NXAST_POP_QUEUE(out);
}


parse_POP_QUEUE(const char *arg OVS_UNUSED,
                const struct ofpact_parse_params *pp)
{
    ofpact_put_POP_QUEUE(pp->ofpacts);
    return NULL;
}


check_CT(struct ofpact_conntrack *a, struct ofpact_check_params *cp)
{
    struct flow *flow = &cp->match->flow;

    if (!dl_type_is_ip_any(get_dl_type(flow))
        || (flow->ct_state & CS_INVALID && a->flags & NX_CT_F_COMMIT)
        || (a->alg == IPPORT_FTP && flow->nw_proto != IPPROTO_TCP)
        || (a->alg == IPPORT_TFTP && flow->nw_proto != IPPROTO_UDP)) {
        /* We can't downgrade to OF1.0 and expect inconsistent CT actions
         * be silently discarded.  Instead, datapath flow install fails, so
         * it is better to flag inconsistent CT actions as hard errors. */
        return OFPERR_OFPBAC_MATCH_INCONSISTENT;
    }

    if (a->zone_src.field) {
        return mf_check_src(&a->zone_src, cp->match);
    }

    return check_subactions(a->actions, ofpact_ct_get_action_len(a), cp);
}


encode_GOTO_TABLE(const struct ofpact_goto_table *goto_table,
                  enum ofp_version ofp_version, struct ofpbuf *out)
{
    if (ofp_version == OFP10_VERSION) {
        struct nx_action_resubmit *nar;

        nar = put_NXAST_RESUBMIT_TABLE(out);
        nar->table = goto_table->table_id;
        nar->in_port = htons(ofp_to_u16(OFPP_IN_PORT));
    } else {
        struct ofp11_instruction_goto_table *oigt;

        oigt = instruction_put_OFPIT11_GOTO_TABLE(out);
        oigt->table_id = goto_table->table_id;
        memset(oigt->pad, 0, sizeof oigt->pad);
    }
}


encode_OUTPUT(const struct ofpact_output *output,
              enum ofp_version ofp_version, struct ofpbuf *out)
{
    if (ofp_version == OFP10_VERSION) {
        struct ofp10_action_output *oao;

        oao = put_OFPAT10_OUTPUT(out);
        oao->port = htons(ofp_to_u16(output->port));
        oao->max_len = htons(output->max_len);
    } else {
        struct ofp11_action_output *oao;

        oao = put_OFPAT11_OUTPUT(out);
        oao->port = ofputil_port_to_ofp11(output->port);
        oao->max_len = htons(output->max_len);
    }
}


decode_OFPAT_RAW10_STRIP_VLAN(struct ofpbuf *out)
{
    ofpact_put_STRIP_VLAN(out)->ofpact.raw = OFPAT_RAW10_STRIP_VLAN;
    return 0;
}


check_SET_FIELD(struct ofpact_set_field *a,
                const struct ofpact_check_params *cp)
{
    const struct mf_field *mf = a->field;
    struct flow *flow = &cp->match->flow;
    ovs_be16 *tci = &flow->vlans[0].tci;

    /* Require OXM_OF_VLAN_VID to have an existing VLAN header. */
    if (!mf_are_prereqs_ok(mf, flow, NULL)
        || (mf->id == MFF_VLAN_VID && !(*tci & htons(VLAN_CFI)))) {
        VLOG_WARN_RL(&rl, "set_field %s lacks correct prerequisites",
                     mf->name);
        return OFPERR_OFPBAC_MATCH_INCONSISTENT;
    }

    /* Remember if we saw a VLAN tag in the flow, to aid translating to
     * OpenFlow 1.1 if need be. */
    a->flow_has_vlan = (*tci & htons(VLAN_CFI)) != 0;
    if (mf->id == MFF_VLAN_TCI) {
        /* The set field may add or remove the VLAN tag,
         * Mark the status temporarily. */
        *tci = a->value->be16;
    }

    return 0;
}


format_NAT(const struct ofpact_nat *a, const struct ofpact_format_params *fp)
{
    ds_put_format(fp->s, "%snat%s", colors.paren, colors.end);

    if (a->flags & (NX_NAT_F_SRC | NX_NAT_F_DST)) {
        ds_put_format(fp->s, "%s(%s", colors.paren, colors.end);
        ds_put_format(fp->s, a->flags & NX_NAT_F_SRC ? "%ssrc%s" : "%sdst%s",
                      colors.param, colors.end);

        if (a->range_af != AF_UNSPEC) {
            ds_put_format(fp->s, "%s=%s", colors.param, colors.end);

            if (a->range_af == AF_INET) {
                ds_put_format(fp->s, IP_FMT, IP_ARGS(a->range.addr.ipv4.min));

                if (a->range.addr.ipv4.max
                    && a->range.addr.ipv4.max != a->range.addr.ipv4.min) {
                    ds_put_format(fp->s, "-"IP_FMT,
                                  IP_ARGS(a->range.addr.ipv4.max));
                }
            } else if (a->range_af == AF_INET6) {
                ipv6_format_addr_bracket(&a->range.addr.ipv6.min, fp->s,
                                        a->range.proto.min);

                if (!ipv6_mask_is_any(&a->range.addr.ipv6.max)
                    && memcmp(&a->range.addr.ipv6.max, &a->range.addr.ipv6.min,
                              sizeof(struct in6_addr)) != 0) {
                    ds_put_char(fp->s, '-');
                    ipv6_format_addr_bracket(&a->range.addr.ipv6.max, fp->s,
                                            a->range.proto.min);
                }
            }
            if (a->range.proto.min) {
                ds_put_char(fp->s, ':');
                ds_put_format(fp->s, "%"PRIu16, a->range.proto.min);

                if (a->range.proto.max
                    && a->range.proto.max != a->range.proto.min) {
                    ds_put_format(fp->s, "-%"PRIu16, a->range.proto.max);
                }
            }
            ds_put_char(fp->s, ',');

            if (a->flags & NX_NAT_F_PERSISTENT) {
                ds_put_format(fp->s, "%spersistent%s,",
                              colors.value, colors.end);
            }
            if (a->flags & NX_NAT_F_PROTO_HASH) {
                ds_put_format(fp->s, "%shash%s,", colors.value, colors.end);
            }
            if (a->flags & NX_NAT_F_PROTO_RANDOM) {
                ds_put_format(fp->s, "%srandom%s,", colors.value, colors.end);
            }
        }
        ds_chomp(fp->s, ',');
        ds_put_format(fp->s, "%s)%s", colors.paren, colors.end);
    }
}


decode_SAMPLE2(const struct nx_action_sample2 *nas,
               enum ofp_raw_action_type raw,
               enum nx_action_sample_direction direction,
               struct ofpact_sample *sample)
{
    sample->ofpact.raw = raw;
    sample->probability = ntohs(nas->probability);
    sample->collector_set_id = ntohl(nas->collector_set_id);
    sample->obs_domain_id = ntohl(nas->obs_domain_id);
    sample->obs_point_id = ntohl(nas->obs_point_id);
    sample->sampling_port = u16_to_ofp(ntohs(nas->sampling_port));
    sample->direction = direction;

    if (sample->probability == 0) {
        return OFPERR_OFPBAC_BAD_ARGUMENT;
    }

    return 0;
}


encode_SAMPLE(const struct ofpact_sample *sample,
              enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out)
{
    if (sample->ofpact.raw == NXAST_RAW_SAMPLE3
        || sample->direction != NX_ACTION_SAMPLE_DEFAULT) {
        encode_SAMPLE2(sample, put_NXAST_SAMPLE3(out));
    } else if (sample->ofpact.raw == NXAST_RAW_SAMPLE2
               || sample->sampling_port != OFPP_NONE) {
        encode_SAMPLE2(sample, put_NXAST_SAMPLE2(out));
    } else {
        struct nx_action_sample *nas = put_NXAST_SAMPLE(out);
        nas->probability = htons(sample->probability);
        nas->collector_set_id = htonl(sample->collector_set_id);
        nas->obs_domain_id = htonl(sample->obs_domain_id);
        nas->obs_point_id = htonl(sample->obs_point_id);
    }
}


encode_OUTPUT_TRUNC(const struct ofpact_output_trunc *output_trunc,
                  enum ofp_version ofp_version OVS_UNUSED,
                  struct ofpbuf *out)
{
    struct nx_action_output_trunc *natrc = put_NXAST_OUTPUT_TRUNC(out);

    natrc->max_len = htonl(output_trunc->max_len);
    natrc->port = htons(ofp_to_u16(output_trunc->port));
}


ofpact_copy(struct ofpbuf *out, const struct ofpact *a)
{
    ofpbuf_put(out, a, OFPACT_ALIGN(a->len));
}


encode_CT(const struct ofpact_conntrack *conntrack,
          enum ofp_version ofp_version, struct ofpbuf *out)
{
    struct nx_action_conntrack *nac;
    const size_t ofs = out->size;
    size_t len;

    nac = put_NXAST_CT(out);
    nac->flags = htons(conntrack->flags);
    if (conntrack->zone_src.field) {
        nac->zone_src = htonl(nxm_header_from_mff(conntrack->zone_src.field));
        nac->zone_ofs_nbits = nxm_encode_ofs_nbits(conntrack->zone_src.ofs,
                                                   conntrack->zone_src.n_bits);
    } else {
        nac->zone_src = htonl(0);
        nac->zone_imm = htons(conntrack->zone_imm);
    }
    nac->recirc_table = conntrack->recirc_table;
    nac->alg = htons(conntrack->alg);

    len = ofpacts_put_openflow_actions(conntrack->actions,
                                       ofpact_ct_get_action_len(conntrack),
                                       out, ofp_version);
    len += sizeof(*nac);
    nac = ofpbuf_at(out, ofs, sizeof(*nac));
    nac->len = htons(len);
}


parse_MULTIPATH(const char *arg, const struct ofpact_parse_params *pp)
{
    return multipath_parse(ofpact_put_MULTIPATH(pp->ofpacts), arg);
}


format_SAMPLE(const struct ofpact_sample *a,
              const struct ofpact_format_params *fp)
{
    ds_put_format(fp->s, "%ssample(%s%sprobability=%s%"PRIu16
                  ",%scollector_set_id=%s%"PRIu32
                  ",%sobs_domain_id=%s%"PRIu32
                  ",%sobs_point_id=%s%"PRIu32,
                  colors.paren, colors.end,
                  colors.param, colors.end, a->probability,
                  colors.param, colors.end, a->collector_set_id,
                  colors.param, colors.end, a->obs_domain_id,
                  colors.param, colors.end, a->obs_point_id);
    if (a->sampling_port != OFPP_NONE) {
        ds_put_format(fp->s, ",%ssampling_port=%s", colors.param, colors.end);
        ofputil_format_port(a->sampling_port, fp->port_map, fp->s);
    }
    if (a->direction == NX_ACTION_SAMPLE_INGRESS) {
        ds_put_format(fp->s, ",%singress%s", colors.param, colors.end);
    } else if (a->direction == NX_ACTION_SAMPLE_EGRESS) {
        ds_put_format(fp->s, ",%segress%s", colors.param, colors.end);
    }
    ds_put_format(fp->s, "%s)%s", colors.paren, colors.end);
}


ovs_instruction_type_from_inst_type(enum ovs_instruction_type *instruction_type,
                                    const uint16_t inst_type)
{
    switch (inst_type) {

#define DEFINE_INST(ENUM, STRUCT, EXTENSIBLE, NAME) \
    case ENUM:                                      \
        *instruction_type = OVSINST_##ENUM;         \
        return 0;
OVS_INSTRUCTIONS
#undef DEFINE_INST

    default:
        return OFPERR_OFPBIC_UNKNOWN_INST;
    }
}


ovs_instruction_type_from_name(const char *name)
{
    const struct instruction_type_info *p;
    for (p = inst_info; p < &inst_info[ARRAY_SIZE(inst_info)]; p++) {
        if (!strcasecmp(name, p->name)) {
            return p->type;
        }
    }
    return -1;
}


ovsinst_bitmap_to_openflow(uint32_t ovsinst_bitmap, enum ofp_version version)
{
    uint32_t ofpit_bitmap = 0;
    const struct ovsinst_map *x;

    for (x = get_ovsinst_map(version); x->ofpit >= 0; x++) {
        if (ovsinst_bitmap & (1u << x->ovsinst)) {
            ofpit_bitmap |= 1u << x->ofpit;
        }
    }
    return htonl(ofpit_bitmap);
}


decode_NXAST_RAW_ENCAP(const struct nx_action_encap *nae,
                       enum ofp_version ofp_version OVS_UNUSED,
                       struct ofpbuf *out)
{
    struct ofpact_encap *encap;
    const struct ofp_ed_prop_header *ofp_prop;
    const size_t encap_ofs = out->size;
    size_t props_len;
    uint16_t n_props = 0;
    int err;

    encap = ofpact_put_ENCAP(out);
    encap->ofpact.raw = NXAST_RAW_ENCAP;
    switch (ntohl(nae->new_pkt_type)) {
    case PT_ETH:
    case PT_NSH:
        /* Add supported encap header types here. */
        break;
    default:
        return OFPERR_NXBAC_BAD_HEADER_TYPE;
    }
    encap->new_pkt_type = nae->new_pkt_type;
    encap->hdr_size = ntohs(nae->hdr_size);

    ofp_prop = nae->props;
    props_len = ntohs(nae->len) - offsetof(struct nx_action_encap, props);
    n_props = 0;
    while (props_len > 0) {
        err = decode_ed_prop(&ofp_prop, out, &props_len);
        if (err) {
            return err;
        }
        n_props++;
    }
    encap = ofpbuf_at_assert(out, encap_ofs, sizeof *encap);
    encap->n_props = n_props;
    out->header = &encap->ofpact;
    ofpact_finish_ENCAP(out, &encap);

    return 0;
}


format_ed_props(struct ds *s, uint16_t n_props,
                const struct ofpact_ed_prop *prop)
{
    const uint8_t *p = (uint8_t *) prop;
    int i;

    if (n_props == 0) {
        return;
    }
    for (i = 0; i < n_props; i++) {
        format_ed_prop(s, prop);
        ds_put_char(s, ',');
        p += ROUND_UP(prop->len, 8);
        prop = ALIGNED_CAST(const struct ofpact_ed_prop *, p);
    }
    if (n_props > 0) {
        ds_chomp(s, ',');
    }
}


ofpact_hdrs_hash(const struct ofpact_hdrs *hdrs)
{
    return hash_2words(hdrs->vendor,
                       ((uint32_t) hdrs->type << 16) | hdrs->ofp_version);
}


check_DECAP(const struct ofpact_decap *a OVS_UNUSED,
            struct ofpact_check_params *cp)
{
    struct flow *flow = &cp->match->flow;
    if (flow->packet_type == htonl(PT_ETH)) {
        /* Adjust the packet_type to allow subsequent actions. */
        flow->packet_type = PACKET_TYPE_BE(OFPHTN_ETHERTYPE,
                                           ntohs(flow->dl_type));
    } else {
        /* The actual packet_type is only known after decapsulation.
         * Do not allow subsequent actions that depend on packet headers. */
        flow->packet_type = htonl(PT_UNKNOWN);
        flow->dl_type = OVS_BE16_MAX;
    }
    return 0;
}


encode_MULTIPATH(const struct ofpact_multipath *mp,
                 enum ofp_version ofp_version OVS_UNUSED, struct ofpbuf *out)
{
    struct nx_action_multipath *nam = put_NXAST_MULTIPATH(out);

    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(nxm_header_from_mff(mp->dst.field));
}


format_GOTO_TABLE(const struct ofpact_goto_table *a,
                  const struct ofpact_format_params *fp)
{
    ds_put_format(fp->s, "%sgoto_table:%s", colors.param, colors.end);
    ofputil_format_table(a->table_id, fp->table_map, fp->s);
}


check_SET_VLAN_VID(struct ofpact_vlan_vid *a, struct ofpact_check_params *cp)
{
    /* Remember if we saw a vlan tag in the flow to aid translating to OpenFlow
     * 1.1+ if need be. */
    ovs_be16 *tci = &cp->match->flow.vlans[0].tci;
    a->flow_has_vlan = (*tci & htons(VLAN_CFI)) != 0;
    if (!a->flow_has_vlan && !a->push_vlan_if_needed) {
        inconsistent_match(&cp->usable_protocols);
    }

    /* Temporarily mark that we have a vlan tag. */
    *tci |= htons(VLAN_CFI);

    return 0;
}


parse_reg_load(char *arg, const struct ofpact_parse_params *pp)
{
    struct mf_subfield dst;
    char *key, *value_str;
    union mf_value value;
    char *error;

    error = set_field_split_str(arg, &key, &value_str, NULL);
    if (error) {
        return error;
    }

    error = mf_parse_subfield(&dst, key);
    if (error) {
        return error;
    }

    if (parse_int_string(value_str, (uint8_t *)&value, dst.field->n_bytes,
                         &key)) {
        return xasprintf("%s: cannot parse integer value", arg);
    }

    if (!bitwise_is_all_zeros(&value, dst.field->n_bytes, dst.n_bits,
                              dst.field->n_bytes * 8 - dst.n_bits)) {
        struct ds ds;

        ds_init(&ds);
        mf_format(dst.field, &value, NULL, NULL, &ds);
        error = xasprintf("%s: value %s does not fit into %d bits",
                          arg, ds_cstr(&ds), dst.n_bits);
        ds_destroy(&ds);
        return error;
    }

    struct ofpact_set_field *sf = ofpact_put_reg_load(pp->ofpacts, dst.field,
                                                      NULL, NULL);

    bitwise_copy(&value, dst.field->n_bytes, 0, sf->value,
                 dst.field->n_bytes, dst.ofs, dst.n_bits);
    bitwise_one(ofpact_set_field_mask(sf), dst.field->n_bytes, dst.ofs,
                dst.n_bits);
    return NULL;
}


parse_STACK_PUSH(char *arg, const struct ofpact_parse_params *pp)
{
    return nxm_parse_stack_action(ofpact_put_STACK_PUSH(pp->ofpacts), arg);
}


put_u16(struct ofpbuf *b, uint16_t x)
{
    put_be16(b, htons(x));
}


parse_CLEAR_ACTIONS(char *arg OVS_UNUSED, const struct ofpact_parse_params *pp)
{
    ofpact_put_CLEAR_ACTIONS(pp->ofpacts);
    return NULL;
}


ofpact_format(const struct ofpact *a,
              const struct ofpact_format_params *fp)
{
    switch (a->type) {
#define OFPACT(ENUM, STRUCT, MEMBER, NAME)                              \
        case OFPACT_##ENUM:                                             \
            format_##ENUM(ALIGNED_CAST(const struct STRUCT *, a), fp);  \
            break;
        OFPACTS
#undef OFPACT
    default:
        OVS_NOT_REACHED();
    }
}


parse_SET_ETH_DST(char *arg, const struct ofpact_parse_params *pp)
{
    return str_to_mac(arg, &ofpact_put_SET_ETH_DST(pp->ofpacts)->mac);
}


ofpacts_parse_copy(const char *s_, const struct ofpact_parse_params *pp,
                   bool allow_instructions, enum ofpact_type outer_action)
{
    char *error, *s;

    *pp->usable_protocols = OFPUTIL_P_ANY;

    s = xstrdup(s_);
    error = ofpacts_parse(s, pp, allow_instructions, outer_action);
    free(s);

    return error;
}


encode_SET_VLAN_VID(const struct ofpact_vlan_vid *vlan_vid,
                    enum ofp_version ofp_version, struct ofpbuf *out)
{
    uint16_t vid = vlan_vid->vlan_vid;

    /* Push a VLAN tag, if none is present and this form of the action calls
     * for such a feature. */
    if (ofp_version > OFP10_VERSION
        && vlan_vid->push_vlan_if_needed
        && !vlan_vid->flow_has_vlan) {
        put_OFPAT11_PUSH_VLAN(out, htons(ETH_TYPE_VLAN_8021Q));
    }

    if (ofp_version == OFP10_VERSION) {
        put_OFPAT10_SET_VLAN_VID(out, vid);
    } else if (ofp_version == OFP11_VERSION) {
        put_OFPAT11_SET_VLAN_VID(out, vid);
    } else {
        put_set_field(out, ofp_version, MFF_VLAN_VID, vid | OFPVID12_PRESENT);
    }
}


ofpact_put_reg_load2(struct ofpbuf *ofpacts, const struct mf_field *field,
                     const void *value, const void *mask)
{
    struct ofpact_set_field *sf = ofpact_put_set_field(ofpacts, field, value,
                                                       mask);
    sf->ofpact.raw = NXAST_RAW_REG_LOAD2;

    return sf;
}


encode_NAT(const struct ofpact_nat *nat,
           enum ofp_version ofp_version OVS_UNUSED,
           struct ofpbuf *out)
{
    struct nx_action_nat *nan;
    const size_t ofs = out->size;
    uint16_t range_present = 0;

    nan = put_NXAST_NAT(out);
    nan->flags = htons(nat->flags);
    if (nat->range_af == AF_INET) {
        if (nat->range.addr.ipv4.min) {
            ovs_be32 *min = ofpbuf_put_uninit(out, sizeof *min);
            *min = nat->range.addr.ipv4.min;
            range_present |= NX_NAT_RANGE_IPV4_MIN;
        }
        if (nat->range.addr.ipv4.max) {
            ovs_be32 *max = ofpbuf_put_uninit(out, sizeof *max);
            *max = nat->range.addr.ipv4.max;
            range_present |= NX_NAT_RANGE_IPV4_MAX;
        }
    } else if (nat->range_af == AF_INET6) {
        if (!ipv6_mask_is_any(&nat->range.addr.ipv6.min)) {
            struct in6_addr *min = ofpbuf_put_uninit(out, sizeof *min);
            *min = nat->range.addr.ipv6.min;
            range_present |= NX_NAT_RANGE_IPV6_MIN;
        }
        if (!ipv6_mask_is_any(&nat->range.addr.ipv6.max)) {
            struct in6_addr *max = ofpbuf_put_uninit(out, sizeof *max);
            *max = nat->range.addr.ipv6.max;
            range_present |= NX_NAT_RANGE_IPV6_MAX;
        }
    }
    if (nat->range_af != AF_UNSPEC) {
        if (nat->range.proto.min) {
            ovs_be16 *min = ofpbuf_put_uninit(out, sizeof *min);
            *min = htons(nat->range.proto.min);
            range_present |= NX_NAT_RANGE_PROTO_MIN;
        }
        if (nat->range.proto.max) {
            ovs_be16 *max = ofpbuf_put_uninit(out, sizeof *max);
            *max = htons(nat->range.proto.max);
            range_present |= NX_NAT_RANGE_PROTO_MAX;
        }
    }
    pad_ofpat(out, ofs);
    nan = ofpbuf_at(out, ofs, sizeof *nan);
    nan->range_present = htons(range_present);
}


check_CONJUNCTION(const struct ofpact_conjunction *a OVS_UNUSED,
                  const struct ofpact_check_params *cp OVS_UNUSED)
{
    return 0;
}


check_MULTIPATH(const struct ofpact_multipath *a,
                const struct ofpact_check_params *cp)
{
    return multipath_check(a, cp->match);
}


ofpact_raw_lookup(enum ofp_version ofp_version, enum ofp_raw_action_type raw)
{
    const struct ofpact_raw_instance *inst;

    HMAP_FOR_EACH_WITH_HASH (inst, encode_node, hash_2words(raw, ofp_version),
                             ofpact_encode_hmap()) {
        if (inst->raw == raw && inst->hdrs.ofp_version == ofp_version) {
            return inst;
        }
    }
    OVS_NOT_REACHED();
}


parse_ENCAP(char *arg, const struct ofpact_parse_params *pp)
{
    struct ofpact_encap *encap;
    char *key, *value, *str;
    char *error = NULL;
    uint16_t prop_class;
    int n_props = 0;

    encap = ofpact_put_ENCAP(pp->ofpacts);
    encap->hdr_size = 0;
    /* Parse encap header type. */
    str = arg;
    if (!ofputil_parse_key_value(&arg, &key, &value)) {
        return xasprintf("Missing encap hdr: %s", str);
    }
    if (!parse_encap_header(key, &encap->new_pkt_type)) {
        return xasprintf("Encap hdr not supported: %s", value);
    }
    if (!parse_ed_prop_class(key, &prop_class)) {
        return xasprintf("Invalid encap prop class: %s", key);
    }
    /* Parse encap properties. */
    error = parse_ed_props(prop_class, &value, &n_props, pp->ofpacts);
    if (error != NULL) {
        return error;
    }
    /* ofpbuf may have been re-allocated. */
    encap = pp->ofpacts->header;
    encap->n_props = n_props;
    ofpact_finish_ENCAP(pp->ofpacts, &encap);
    return NULL;
}


parse_GROUP(char *arg, const struct ofpact_parse_params *pp)
{
    return str_to_u32(arg, &ofpact_put_GROUP(pp->ofpacts)->group_id);
}


format_CONJUNCTION(const struct ofpact_conjunction *oc,
                   const struct ofpact_format_params *fp)
{
    ds_put_format(fp->s, "%sconjunction(%s%"PRIu32",%d/%"PRIu8"%s)%s",
                  colors.paren, colors.end,
                  oc->id, oc->clause + 1, oc->n_clauses,
                  colors.paren, colors.end);
}


encode_SET_FIELD(const struct ofpact_set_field *sf,
                 enum ofp_version ofp_version, struct ofpbuf *out)
{
    if (ofp_version >= OFP15_VERSION) {
        /* OF1.5+ only has Set-Field (reg_load is redundant so we drop it
         * entirely). */
        set_field_to_set_field(sf, ofp_version, out);
    } else if (sf->ofpact.raw == NXAST_RAW_REG_LOAD ||
               sf->ofpact.raw == NXAST_RAW_REG_LOAD2) {
        /* It came in as reg_load, send it out the same way. */
        set_field_to_nxast(sf, out);
    } else if (ofp_version < OFP12_VERSION) {
        /* OpenFlow 1.0 and 1.1 don't have Set-Field. */
        set_field_to_legacy_openflow(sf, ofp_version, out);
    } else if (is_all_ones(ofpact_set_field_mask(sf), sf->field->n_bytes)) {
        /* We're encoding to OpenFlow 1.2, 1.3, or 1.4.  The action sets an
         * entire field, so encode it as OFPAT_SET_FIELD. */
        set_field_to_set_field(sf, ofp_version, out);
    } else {
        /* We're encoding to OpenFlow 1.2, 1.3, or 1.4.  The action cannot be
         * encoded as OFPAT_SET_FIELD because it does not set an entire field,
         * so encode it as reg_load. */
        set_field_to_nxast(sf, out);
    }
}


decode_NXAST_RAW_CT(const struct nx_action_conntrack *nac,
                    enum ofp_version ofp_version,
                    const struct vl_mff_map *vl_mff_map, uint64_t *tlv_bitmap,
                    struct ofpbuf *out)
{
    const size_t ct_offset = ofpacts_pull(out);
    struct ofpact_conntrack *conntrack = ofpact_put_CT(out);
    int error;

    conntrack->flags = ntohs(nac->flags);
    if (conntrack->flags & NX_CT_F_FORCE &&
        !(conntrack->flags & NX_CT_F_COMMIT)) {
        error = OFPERR_OFPBAC_BAD_ARGUMENT;
        goto out;
    }

    error = decode_ct_zone(nac, conntrack, vl_mff_map, tlv_bitmap);
    if (error) {
        goto out;
    }
    conntrack->recirc_table = nac->recirc_table;
    conntrack->alg = ntohs(nac->alg);

    ofpbuf_pull(out, sizeof(*conntrack));

    struct ofpbuf openflow = ofpbuf_const_initializer(
        nac + 1, ntohs(nac->len) - sizeof(*nac));
    error = ofpacts_pull_openflow_actions__(&openflow, openflow.size,
                                            ofp_version,
                                            1u << OVSINST_OFPIT11_APPLY_ACTIONS,
                                            out, OFPACT_CT, vl_mff_map,
                                            tlv_bitmap);
    if (error) {
        return error;
    }

    conntrack = ofpbuf_push_uninit(out, sizeof(*conntrack));
    out->header = &conntrack->ofpact;
    ofpact_finish_CT(out, &conntrack);

    if (conntrack->ofpact.len > sizeof(*conntrack)
        && !(conntrack->flags & NX_CT_F_COMMIT)) {
        const struct ofpact *a;
        size_t ofpacts_len = conntrack->ofpact.len - sizeof(*conntrack);

        OFPACT_FOR_EACH (a, conntrack->actions, ofpacts_len) {
            if (a->type != OFPACT_NAT || ofpact_get_NAT(a)->flags
                || ofpact_get_NAT(a)->range_af != AF_UNSPEC) {
                VLOG_WARN_RL(&rl, "CT action requires commit flag if actions "
                             "other than NAT without arguments are specified.");
                error = OFPERR_OFPBAC_BAD_ARGUMENT;
                goto out;
            }
        }
    }

out:
    ofpbuf_push_uninit(out, ct_offset);
    return error;
}


format_CT_CLEAR(const struct ofpact_null *a OVS_UNUSED,
                const struct ofpact_format_params *fp)
{
    ds_put_format(fp->s, "%sct_clear%s", colors.value, colors.end);
}


ofpacts_equal_stringwise(const struct ofpact *a, size_t a_len,
                         const struct ofpact *b, size_t b_len)
{
    struct ds a_s = DS_EMPTY_INITIALIZER;
    struct ofpact_format_params a_fp = { .s = &a_s };
    ofpacts_format(a, a_len, &a_fp);

    struct ds b_s = DS_EMPTY_INITIALIZER;
    struct ofpact_format_params b_fp = { .s = &b_s };
    ofpacts_format(b, b_len, &b_fp);

    bool equal = !strcmp(ds_cstr(&a_s), ds_cstr(&b_s));

    ds_destroy(&a_s);
    ds_destroy(&b_s);

    return equal;
}


get_be32(const void **pp)
{
    const ovs_be32 *p = *pp;
    ovs_be32 value = get_unaligned_be32(p);
    *pp = p + 1;
    return value;
}


decode_NXAST_RAW_LEARN(const struct nx_action_learn *nal,
                       enum ofp_version ofp_version OVS_UNUSED,
                       const struct vl_mff_map *vl_mff_map,
                       uint64_t *tlv_bitmap, struct ofpbuf *ofpacts)
{
    struct ofpact_learn *learn;
    enum ofperr error;

    learn = ofpact_put_LEARN(ofpacts);

    error = decode_LEARN_common(nal, NXAST_RAW_LEARN, learn);
    if (error) {
        return error;
    }

    if (learn->flags & ~(NX_LEARN_F_SEND_FLOW_REM |
                         NX_LEARN_F_DELETE_LEARNED)) {
        return OFPERR_OFPBAC_BAD_ARGUMENT;
    }

    return decode_LEARN_specs(nal + 1, (char *) nal + ntohs(nal->len),
                              vl_mff_map, tlv_bitmap, ofpacts);
}


decode_NXAST_RAW_DEC_NSH_TTL(struct ofpbuf *out)
{
    ofpact_put_DEC_NSH_TTL(out);
    return 0;
}


parse_set_vlan_pcp(char *arg, bool push_vlan_if_needed,
                   const struct ofpact_parse_params *pp)
{
    struct ofpact_vlan_pcp *vlan_pcp;
    uint8_t pcp;
    char *error;

    error = str_to_u8(arg, "VLAN PCP", &pcp);
    if (error) {
        return error;
    }

    if (pcp & ~7) {
        return xasprintf("%s: not a valid VLAN PCP", arg);
    }
    vlan_pcp = ofpact_put_SET_VLAN_PCP(pp->ofpacts);
    vlan_pcp->vlan_pcp = pcp;
    vlan_pcp->push_vlan_if_needed = push_vlan_if_needed;
    return NULL;
}


next_load_segment(const struct ofpact_set_field *sf,
                  struct mf_subfield *dst, uint64_t *value)
{
    int n_bits = sf->field->n_bits;
    int n_bytes = sf->field->n_bytes;
    int start = dst->ofs + dst->n_bits;

    if (start < n_bits) {
        dst->field = sf->field;
        dst->ofs = bitwise_scan(ofpact_set_field_mask(sf), n_bytes, 1, start,
                                n_bits);
        if (dst->ofs < n_bits) {
            dst->n_bits = bitwise_scan(ofpact_set_field_mask(sf), n_bytes, 0,
                                       dst->ofs + 1,
                                       MIN(dst->ofs + 64, n_bits)) - dst->ofs;
            *value = bitwise_get(sf->value, n_bytes, dst->ofs, dst->n_bits);
            return true;
        }
    }
    return false;
}


decode_NXAST_RAW_NOTE(const struct nx_action_note *nan,
                      enum ofp_version ofp_version OVS_UNUSED,
                      struct ofpbuf *out)
{
    struct ofpact_note *note;
    unsigned int length;

    length = ntohs(nan->len) - offsetof(struct nx_action_note, note);
    note = ofpact_put_NOTE(out);
    note->length = length;
    ofpbuf_put(out, nan->note, length);
    note = out->header;
    ofpact_finish_NOTE(out, &note);

    return 0;
}


format_encap_pkt_type(const ovs_be32 pkt_type)
{
    switch (ntohl(pkt_type)) {
    case PT_ETH:
        return "ethernet";
    case PT_NSH:
        return "nsh";
    default:
        return "UNKNOWN";
    }
}


ofpacts_format(const struct ofpact *ofpacts, size_t ofpacts_len,
               const struct ofpact_format_params *fp)
{
    if (!ofpacts_len) {
        ds_put_format(fp->s, "%sdrop%s", colors.drop, colors.end);
    } else {
        const struct ofpact *a;

        OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
            if (a != ofpacts) {
                ds_put_char(fp->s, ',');
            }

            ofpact_format(a, fp);
        }
    }
}


encode_DEBUG_RECIRC(const struct ofpact_null *n OVS_UNUSED,
                    enum ofp_version ofp_version OVS_UNUSED,
                    struct ofpbuf *out)
{
    put_NXAST_DEBUG_RECIRC(out);
}


unsupported_nesting(enum ofpact_type action, enum ofpact_type outer_action)
{
    VLOG_WARN("%s action doesn't support nested action %s",
              ofpact_name(outer_action), ofpact_name(action));
    return OFPERR_OFPBAC_BAD_ARGUMENT;
}