Commit message: ofp-actions: Avoid buffer overread in BUNDLE action decoding.

Reported-at: https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=9052
Signed-off-by: Ben Pfaff <blp@ovn.org>
Acked-by: Justin Pettit <jpettit@ovn.org>
CVE: CVE-2018-17206
CWE: CWE-125
Vulnerability description: An issue was discovered in Open vSwitch (OvS) 2.7.x through 2.7.6. The decode_bundle function inside lib/ofp-actions.c is affected by a buffer over-read issue during BUNDLE action decoding.

Project: ovs
File: ofp-actions.c

decode_NXAST_RAW_CONJUNCTION(const struct nx_action_conjunction *nac,
                             enum ofp_version ofp_version OVS_UNUSED,
                             struct ofpbuf *out)
{
    if (nac->n_clauses < 2 || nac->n_clauses > 64
        || nac->clause >= nac->n_clauses) {
        return OFPERR_NXBAC_BAD_CONJUNCTION;
    } else {
        add_conjunction(out, ntohl(nac->id), nac->clause, nac->n_clauses);
        return 0;
    }
}

Project: ovs
File: ofp-actions.c

decode_NXAST_RAW_CONTROLLER(const struct nx_action_controller *nac,
                            enum ofp_version ofp_version OVS_UNUSED,
                            struct ofpbuf *out)
{
    struct ofpact_controller *oc;

    oc = ofpact_put_CONTROLLER(out);
    oc->ofpact.raw = NXAST_RAW_CONTROLLER;
    oc->max_len = ntohs(nac->max_len);
    oc->controller_id = ntohs(nac->controller_id);
    oc->reason = nac->reason;
    ofpact_finish_CONTROLLER(out, &oc);

    return 0;
}

Project: ovs
File: ofp-actions.c

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);
}

Project: ovs
File: ofp-actions.c

decode_NXAST_RAW_REG_LOAD(const struct nx_action_reg_load *narl,
                          enum ofp_version ofp_version OVS_UNUSED,
                          const struct vl_mff_map *vl_mff_map,
                          uint64_t *tlv_bitmap, struct ofpbuf *out)
{
    struct mf_subfield dst;
    enum ofperr error;

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

    error = mf_check_dst(&dst, NULL);
    if (error) {
        return error;
    }

    /* Reject 'narl' if a bit numbered 'n_bits' or higher is set to 1 in
     * narl->value. */
    if (dst.n_bits < 64 && ntohll(narl->value) >> dst.n_bits) {
        return OFPERR_OFPBAC_BAD_ARGUMENT;
    }

    struct ofpact_set_field *sf = ofpact_put_reg_load(out, dst.field, NULL,
                                                      NULL);
    bitwise_put(ntohll(narl->value),
                sf->value, dst.field->n_bytes, dst.ofs,
                dst.n_bits);
    bitwise_put(UINT64_MAX,
                ofpact_set_field_mask(sf), dst.field->n_bytes, dst.ofs,
                dst.n_bits);
    return 0;
}

Project: ovs
File: ofp-actions.c

decode_OFPAT_RAW_SET_MPLS_TC(uint8_t tc,
                             enum ofp_version ofp_version OVS_UNUSED,
                             struct ofpbuf *out)
{
    ofpact_put_SET_MPLS_TC(out)->tc = tc;
    return 0;
}

Project: ovs
File: ofp-actions.c

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;
}

Project: ovs
File: ofp-actions.c

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);
}

Project: ovs
File: ofp-actions.c

encode_BUNDLE(const struct ofpact_bundle *bundle,
              enum ofp_version ofp_version OVS_UNUSED,
              struct ofpbuf *out)
{
    int slaves_len = ROUND_UP(2 * bundle->n_slaves, OFP_ACTION_ALIGN);
    struct nx_action_bundle *nab;
    ovs_be16 *slaves;
    size_t i;

    nab = (bundle->dst.field
           ? put_NXAST_BUNDLE_LOAD(out)
           : put_NXAST_BUNDLE(out));
    nab->len = htons(ntohs(nab->len) + slaves_len);
    nab->algorithm = htons(bundle->algorithm);
    nab->fields = htons(bundle->fields);
    nab->basis = htons(bundle->basis);
    nab->slave_type = htonl(mf_nxm_header(MFF_IN_PORT));
    nab->n_slaves = htons(bundle->n_slaves);
    if (bundle->dst.field) {
        nab->ofs_nbits = nxm_encode_ofs_nbits(bundle->dst.ofs,
                                              bundle->dst.n_bits);
        nab->dst = htonl(nxm_header_from_mff(bundle->dst.field));
    }

    slaves = ofpbuf_put_zeros(out, slaves_len);
    for (i = 0; i < bundle->n_slaves; i++) {
        slaves[i] = htons(ofp_to_u16(bundle->slaves[i]));
    }
}

Project: ovs
File: ofp-actions.c

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

Project: ovs
File: ofp-actions.c

encode_SET_L4_port(const struct ofpact_l4_port *l4_port,
                   enum ofp_version ofp_version, enum ofp_raw_action_type raw,
                   enum mf_field_id field, struct ofpbuf *out)
{
    uint16_t port = l4_port->port;

    if (ofp_version >= OFP12_VERSION && field != MFF_N_IDS) {
        put_set_field(out, ofp_version, field, port);
    } else {
        ofpact_put_raw(out, ofp_version, raw, port);
    }
}

Project: ovs
File: ofp-actions.c

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);
    }
}

Project: ovs
File: ofp-actions.c

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);
    }
}

Project: ovs
File: ofp-actions.c

encode_STRIP_VLAN(const struct ofpact_null *null OVS_UNUSED,
                  enum ofp_version ofp_version, struct ofpbuf *out)
{
    if (ofp_version == OFP10_VERSION) {
        put_OFPAT10_STRIP_VLAN(out);
    } else {
        put_OFPAT11_POP_VLAN(out);
    }
}

Project: ovs
File: ofp-actions.c

encode_ofpact(const struct ofpact *a, enum ofp_version ofp_version,
              struct ofpbuf *out)
{
    switch (a->type) {
#define OFPACT(ENUM, STRUCT, MEMBER, NAME)                              \
        case OFPACT_##ENUM:                                             \
            encode_##ENUM(ofpact_get_##ENUM(a), ofp_version, out);      \
            return;
        OFPACTS
#undef OFPACT
    default:
        OVS_NOT_REACHED();
    }
}

Project: ovs
File: ofp-actions.c

format_CONTROLLER(const struct ofpact_controller *a, struct ds *s)
{
    if (a->reason == OFPR_ACTION && !a->controller_id && !a->userdata_len
        && !a->pause) {
        ds_put_format(s, "%sCONTROLLER:%s%"PRIu16,
                      colors.special, colors.end, a->max_len);
    } else {
        enum ofp_packet_in_reason reason = a->reason;

        ds_put_format(s, "%scontroller(%s", colors.paren, colors.end);
        if (reason != OFPR_ACTION) {
            char reasonbuf[OFPUTIL_PACKET_IN_REASON_BUFSIZE];

            ds_put_format(s, "%sreason=%s%s,", colors.param, colors.end,
                          ofputil_packet_in_reason_to_string(
                              reason, reasonbuf, sizeof reasonbuf));
        }
        if (a->max_len != UINT16_MAX) {
            ds_put_format(s, "%smax_len=%s%"PRIu16",",
                          colors.param, colors.end, a->max_len);
        }
        if (a->controller_id != 0) {
            ds_put_format(s, "%sid=%s%"PRIu16",",
                          colors.param, colors.end, a->controller_id);
        }
        if (a->userdata_len) {
            ds_put_format(s, "%suserdata=%s", colors.param, colors.end);
            format_hex_arg(s, a->userdata, a->userdata_len);
            ds_put_char(s, ',');
        }
        if (a->pause) {
            ds_put_format(s, "%spause%s,", colors.value, colors.end);
        }
        ds_chomp(s, ',');
        ds_put_format(s, "%s)%s", colors.paren, colors.end);
    }
}

Project: ovs
File: ofp-actions.c

format_LEARN(const struct ofpact_learn *a, struct ds *s)
{
    learn_format(a, s);
}

Project: ovs
File: ofp-actions.c

format_RESUBMIT(const struct ofpact_resubmit *a, struct ds *s)
{
    if (a->in_port != OFPP_IN_PORT && a->table_id == 255) {
        ds_put_format(s, "%sresubmit:%s", colors.special, colors.end);
        ofputil_format_port(a->in_port, s);
    } else {
        ds_put_format(s, "%sresubmit(%s", colors.paren, colors.end);
        if (a->in_port != OFPP_IN_PORT) {
            ofputil_format_port(a->in_port, s);
        }
        ds_put_char(s, ',');
        if (a->table_id != 255) {
            ds_put_format(s, "%"PRIu8, a->table_id);
        }
        ds_put_format(s, "%s)%s", colors.paren, colors.end);
    }
}

Project: ovs
File: ofp-actions.c

format_SET_FIELD(const struct ofpact_set_field *a, struct ds *s)
{
    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(s, "%sload:%s%#"PRIx64"%s->%s",
                          colors.special, colors.end, value,
                          colors.special, colors.end);
            mf_format_subfield(&dst, s);
            ds_put_char(s, ',');
        }
        ds_chomp(s, ',');
    } else {
        ds_put_format(s, "%sset_field:%s", colors.special, colors.end);
        mf_format(a->field, a->value, ofpact_set_field_mask(a), s);
        ds_put_format(s, "%s->%s%s",
                      colors.special, colors.end, a->field->name);
    }
}

Project: ovs
File: ofp-actions.c

format_SET_QUEUE(const struct ofpact_queue *a, struct ds *s)
{
    ds_put_format(s, "%sset_queue:%s%"PRIu32,
                  colors.param, colors.end, a->queue_id);
}

Project: ovs
File: ofp-actions.c

get_be16(const void **pp)
{
    const ovs_be16 *p = *pp;
    ovs_be16 value = *p;
    *pp = p + 1;
    return value;
}

Project: ovs
File: ofp-actions.c

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

Project: ovs
File: ofp-actions.c

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, ' ');
    }
}

Project: ovs
File: ofp-actions.c

ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
               struct flow *flow, ofp_port_t max_ports,
               uint8_t table_id, uint8_t n_tables)
{
    const struct ofpact_enqueue *enqueue;
    const struct mf_field *mf;

    switch (a->type) {
    case OFPACT_OUTPUT:
        return ofpact_check_output_port(ofpact_get_OUTPUT(a)->port,
                                        max_ports);

    case OFPACT_CONTROLLER:
        return 0;

    case OFPACT_ENQUEUE:
        enqueue = ofpact_get_ENQUEUE(a);
        if (ofp_to_u16(enqueue->port) >= ofp_to_u16(max_ports)
            && enqueue->port != OFPP_IN_PORT
            && enqueue->port != OFPP_LOCAL) {
            return OFPERR_OFPBAC_BAD_OUT_PORT;
        }
        return 0;

    case OFPACT_OUTPUT_REG:
        return mf_check_src(&ofpact_get_OUTPUT_REG(a)->src, flow);

    case OFPACT_OUTPUT_TRUNC:
        return ofpact_check_output_port(ofpact_get_OUTPUT_TRUNC(a)->port,
                                        max_ports);

    case OFPACT_BUNDLE:
        return bundle_check(ofpact_get_BUNDLE(a), max_ports, flow);

    case OFPACT_SET_VLAN_VID:
        /* Remember if we saw a vlan tag in the flow to aid translating to
         * OpenFlow 1.1+ if need be. */
        ofpact_get_SET_VLAN_VID(a)->flow_has_vlan =
            (flow->vlan_tci & htons(VLAN_CFI)) == htons(VLAN_CFI);
        if (!(flow->vlan_tci & htons(VLAN_CFI)) &&
            !ofpact_get_SET_VLAN_VID(a)->push_vlan_if_needed) {
            inconsistent_match(usable_protocols);
        }
        /* Temporary mark that we have a vlan tag. */
        flow->vlan_tci |= htons(VLAN_CFI);
        return 0;

    case OFPACT_SET_VLAN_PCP:
        /* Remember if we saw a vlan tag in the flow to aid translating to
         * OpenFlow 1.1+ if need be. */
        ofpact_get_SET_VLAN_PCP(a)->flow_has_vlan =
            (flow->vlan_tci & htons(VLAN_CFI)) == htons(VLAN_CFI);
        if (!(flow->vlan_tci & htons(VLAN_CFI)) &&
            !ofpact_get_SET_VLAN_PCP(a)->push_vlan_if_needed) {
            inconsistent_match(usable_protocols);
        }
        /* Temporary mark that we have a vlan tag. */
        flow->vlan_tci |= htons(VLAN_CFI);
        return 0;

    case OFPACT_STRIP_VLAN:
        if (!(flow->vlan_tci & htons(VLAN_CFI))) {
            inconsistent_match(usable_protocols);
        }
        /* Temporary mark that we have no vlan tag. */
        flow->vlan_tci = htons(0);
        return 0;

    case OFPACT_PUSH_VLAN:
        if (flow->vlan_tci & htons(VLAN_CFI)) {
            /* Multiple VLAN headers not supported. */
            return OFPERR_OFPBAC_BAD_TAG;
        }
        /* Temporary mark that we have a vlan tag. */
        flow->vlan_tci |= htons(VLAN_CFI);
        return 0;

    case OFPACT_SET_ETH_SRC:
    case OFPACT_SET_ETH_DST:
        return 0;

    case OFPACT_SET_IPV4_SRC:
    case OFPACT_SET_IPV4_DST:
        if (flow->dl_type != htons(ETH_TYPE_IP)) {
            inconsistent_match(usable_protocols);
        }
        return 0;

    case OFPACT_SET_IP_DSCP:
    case OFPACT_SET_IP_ECN:
    case OFPACT_SET_IP_TTL:
    case OFPACT_DEC_TTL:
        if (!is_ip_any(flow)) {
            inconsistent_match(usable_protocols);
        }
        return 0;

    case OFPACT_SET_L4_SRC_PORT:
    case OFPACT_SET_L4_DST_PORT:
        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(usable_protocols);
        }
        /* Note on which transport protocol the port numbers are set.
         * This allows this set action to be converted to an OF1.2 set field
         * action. */
        if (a->type == OFPACT_SET_L4_SRC_PORT) {
            ofpact_get_SET_L4_SRC_PORT(a)->flow_ip_proto = flow->nw_proto;
        } else {
            ofpact_get_SET_L4_DST_PORT(a)->flow_ip_proto = flow->nw_proto;
        }
        return 0;

    case OFPACT_REG_MOVE:
        return nxm_reg_move_check(ofpact_get_REG_MOVE(a), flow);

    case OFPACT_SET_FIELD:
        mf = ofpact_get_SET_FIELD(a)->field;
        /* Require OXM_OF_VLAN_VID to have an existing VLAN header. */
        if (!mf_are_prereqs_ok(mf, flow, NULL) ||
            (mf->id == MFF_VLAN_VID && !(flow->vlan_tci & htons(VLAN_CFI)))) {
            VLOG_WARN_RL(&rl, "set_field %s lacks correct prerequisities",
                         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. */
        ofpact_get_SET_FIELD(a)->flow_has_vlan =
            (flow->vlan_tci & htons(VLAN_CFI)) == htons(VLAN_CFI);
        if (mf->id == MFF_VLAN_TCI) {
            /* The set field may add or remove the vlan tag,
             * Mark the status temporarily. */
            flow->vlan_tci = ofpact_get_SET_FIELD(a)->value->be16;
        }
        return 0;

    case OFPACT_STACK_PUSH:
        return nxm_stack_push_check(ofpact_get_STACK_PUSH(a), flow);

    case OFPACT_STACK_POP:
        return nxm_stack_pop_check(ofpact_get_STACK_POP(a), flow);

    case OFPACT_SET_MPLS_LABEL:
    case OFPACT_SET_MPLS_TC:
    case OFPACT_SET_MPLS_TTL:
    case OFPACT_DEC_MPLS_TTL:
        if (!eth_type_mpls(flow->dl_type)) {
            inconsistent_match(usable_protocols);
        }
        return 0;

    case OFPACT_SET_TUNNEL:
    case OFPACT_SET_QUEUE:
    case OFPACT_POP_QUEUE:
    case OFPACT_RESUBMIT:
        return 0;

    case OFPACT_FIN_TIMEOUT:
        if (flow->nw_proto != IPPROTO_TCP) {
            inconsistent_match(usable_protocols);
        }
        return 0;

    case OFPACT_LEARN:
        return learn_check(ofpact_get_LEARN(a), flow);

    case OFPACT_CONJUNCTION:
        return 0;

    case OFPACT_MULTIPATH:
        return multipath_check(ofpact_get_MULTIPATH(a), flow);

    case OFPACT_NOTE:
    case OFPACT_EXIT:
        return 0;

    case OFPACT_PUSH_MPLS:
        flow->dl_type = ofpact_get_PUSH_MPLS(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;

    case OFPACT_POP_MPLS:
        if (!eth_type_mpls(flow->dl_type)) {
            inconsistent_match(usable_protocols);
        }
        flow->dl_type = ofpact_get_POP_MPLS(a)->ethertype;
        return 0;

    case OFPACT_SAMPLE:
        return 0;

    case OFPACT_CLONE: {
        struct ofpact_nest *on = ofpact_get_CLONE(a);
        return ofpacts_check(on->actions, ofpact_nest_get_action_len(on),
                             flow, max_ports, table_id, n_tables,
                             usable_protocols);
    }

    case OFPACT_CT: {
        struct ofpact_conntrack *oc = ofpact_get_CT(a);

        if (!dl_type_is_ip_any(flow->dl_type)
            || (flow->ct_state & CS_INVALID && oc->flags & NX_CT_F_COMMIT)
            || (oc->alg == IPPORT_FTP && flow->nw_proto != IPPROTO_TCP)
            || (oc->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 (oc->zone_src.field) {
            return mf_check_src(&oc->zone_src, flow);
        }

        return ofpacts_check(oc->actions, ofpact_ct_get_action_len(oc),
                             flow, max_ports, table_id, n_tables,
                             usable_protocols);
    }

    case OFPACT_CT_CLEAR:
        return 0;

    case OFPACT_NAT: {
        struct ofpact_nat *on = ofpact_get_NAT(a);

        if (!dl_type_is_ip_any(flow->dl_type) ||
            (on->range_af == AF_INET && flow->dl_type != htons(ETH_TYPE_IP)) ||
            (on->range_af == AF_INET6
             && flow->dl_type != htons(ETH_TYPE_IPV6))) {
            return OFPERR_OFPBAC_MATCH_INCONSISTENT;
        }
        return 0;
    }

    case OFPACT_CLEAR_ACTIONS:
        return 0;

    case OFPACT_WRITE_ACTIONS: {
        /* Use a temporary copy of 'usable_protocols' because we can't check
         * consistency of an action set. */
        struct ofpact_nest *on = ofpact_get_WRITE_ACTIONS(a);
        enum ofputil_protocol p = *usable_protocols;
        return ofpacts_check(on->actions, ofpact_nest_get_action_len(on),
                             flow, max_ports, table_id, n_tables, &p);
    }

    case OFPACT_WRITE_METADATA:
        return 0;

    case OFPACT_METER: {
        uint32_t mid = ofpact_get_METER(a)->meter_id;
        if (mid == 0 || mid > OFPM13_MAX) {
            return OFPERR_OFPMMFC_INVALID_METER;
        }
        return 0;
    }

    case OFPACT_GOTO_TABLE: {
        uint8_t goto_table = ofpact_get_GOTO_TABLE(a)->table_id;
        if ((table_id != 255 && goto_table <= table_id)
            || (n_tables != 255 && goto_table >= n_tables)) {
            return OFPERR_OFPBIC_BAD_TABLE_ID;
        }
        return 0;
    }

    case OFPACT_GROUP:
        return 0;

    case OFPACT_UNROLL_XLATE:
        /* UNROLL is an internal action that should never be seen via
         * OpenFlow. */
        return OFPERR_OFPBAC_BAD_TYPE;

    case OFPACT_DEBUG_RECIRC:
        return 0;

    default:
        OVS_NOT_REACHED();
    }
}

Project: ovs
File: ofp-actions.c

ofpact_encode_hmap(void)
{
    static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
    static struct hmap hmap;

    if (ovsthread_once_start(&once)) {
        struct ofpact_raw_instance *inst;

        hmap_init(&hmap);
        for (inst = all_raw_instances;
             inst < &all_raw_instances[ARRAY_SIZE(all_raw_instances)];
             inst++) {
            hmap_insert(&hmap, &inst->encode_node,
                        hash_2words(inst->raw, inst->hdrs.ofp_version));
        }
        ovsthread_once_done(&once);
    }
    return &hmap;
}

Project: ovs
File: ofp-actions.c

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

Project: ovs
File: ofp-actions.c

ofpacts_output_to_port(const struct ofpact *ofpacts, size_t ofpacts_len,
                       ofp_port_t port)
{
    const struct ofpact *a;

    OFPACT_FOR_EACH_FLATTENED (a, ofpacts, ofpacts_len) {
        if (ofpact_outputs_to_port(a, port)) {
            return true;
        }
    }

    return false;
}

Project: ovs
File: ofp-actions.c

ofpacts_parse(char *str, struct ofpbuf *ofpacts,
              enum ofputil_protocol *usable_protocols, bool allow_instructions,
              enum ofpact_type outer_action)
{
    uint32_t orig_size = ofpacts->size;
    char *error = ofpacts_parse__(str, ofpacts, usable_protocols,
                                  allow_instructions, outer_action);
    if (error) {
        ofpacts->size = orig_size;
    }
    return error;
}

Project: ovs
File: ofp-actions.c

ovs_instruction_name_from_type(enum ovs_instruction_type type)
{
    return type < ARRAY_SIZE(inst_info) ? inst_info[type].name : NULL;
}

Project: ovs
File: ofp-actions.c

parse_CLONE(char *arg, struct ofpbuf *ofpacts,
             enum ofputil_protocol *usable_protocols)
{
    const size_t clone_offset = ofpacts_pull(ofpacts);
    struct ofpact_nest *clone = ofpact_put_CLONE(ofpacts);
    char *error;

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

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

Project: ovs
File: ofp-actions.c

parse_ENQUEUE(char *arg, struct ofpbuf *ofpacts,
              enum ofputil_protocol *usable_protocols OVS_UNUSED)
{
    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(ofpacts);
    if (!ofputil_port_from_string(port, &enqueue->port)) {
        return xasprintf("%s: enqueue to unknown port", port);
    }
    return str_to_u32(queue, &enqueue->queue);
}

Project: ovs
File: ofp-actions.c

parse_REG_MOVE(const char *arg, struct ofpbuf *ofpacts,
               enum ofputil_protocol *usable_protocols OVS_UNUSED)
{
    struct ofpact_reg_move *move = ofpact_put_REG_MOVE(ofpacts);
    return nxm_parse_reg_move(move, arg);
}

Project: ovs
File: ofp-actions.c

parse_SAMPLE(char *arg, struct ofpbuf *ofpacts,
             enum ofputil_protocol *usable_protocols OVS_UNUSED)
{
    struct ofpact_sample *os = ofpact_put_SAMPLE(ofpacts);
    os->sampling_port = OFPP_NONE;
    os->direction = NX_ACTION_SAMPLE_DEFAULT;

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

        if (!strcmp(key, "probability")) {
            error = str_to_u16(value, "probability", &os->probability);
            if (!error && os->probability == 0) {
                error = xasprintf("invalid probability value \"%s\"", value);
            }
        } else if (!strcmp(key, "collector_set_id")) {
            error = str_to_u32(value, &os->collector_set_id);
        } else if (!strcmp(key, "obs_domain_id")) {
            error = str_to_u32(value, &os->obs_domain_id);
        } else if (!strcmp(key, "obs_point_id")) {
            error = str_to_u32(value, &os->obs_point_id);
        } else if (!strcmp(key, "sampling_port")) {
            if (!ofputil_port_from_string(value, &os->sampling_port)) {
                error = xasprintf("%s: unknown port", value);
            }
        } else if (!strcmp(key, "ingress")) {
            os->direction = NX_ACTION_SAMPLE_INGRESS;
        } else if (!strcmp(key, "egress")) {
            os->direction = NX_ACTION_SAMPLE_EGRESS;
        } else {
            error = xasprintf("invalid key \"%s\" in \"sample\" argument",
                              key);
        }
        if (error) {
            return error;
        }
    }
    if (os->probability == 0) {
        return xstrdup("non-zero \"probability\" must be specified on sample");
    }

    return NULL;
}

Project: ovs
File: ofp-actions.c

parse_SET_ETH_DST(char *arg, struct ofpbuf *ofpacts,
                 enum ofputil_protocol *usable_protocols OVS_UNUSED)
{
    return str_to_mac(arg, &ofpact_put_SET_ETH_DST(ofpacts)->mac);
}

Project: ovs
File: ofp-actions.c

parse_SET_IP_TTL(char *arg, struct ofpbuf *ofpacts,
                  enum ofputil_protocol *usable_protocols OVS_UNUSED)
{
    uint8_t ttl;
    char *error;

    error = str_to_u8(arg, "TTL", &ttl);
    if (error) {
        return error;
    }

    ofpact_put_SET_IP_TTL(ofpacts)->ttl = ttl;
    return NULL;
}

Project: ovs
File: ofp-actions.c

parse_WRITE_ACTIONS(char *arg, struct ofpbuf *ofpacts,
                    enum ofputil_protocol *usable_protocols)
{
    size_t ofs = ofpacts_pull(ofpacts);
    struct ofpact_nest *on;
    char *error;

    /* Add a Write-Actions instruction and then pull it off. */
    ofpact_put(ofpacts, OFPACT_WRITE_ACTIONS, sizeof *on);
    ofpbuf_pull(ofpacts, sizeof *on);

    /* Parse nested actions.
     *
     * We pulled off "write-actions" and the previous actions because the
     * OFPACT_WRITE_ACTIONS is only partially constructed: its length is such
     * that it doesn't actually include the nested actions.  That means that
     * ofpacts_parse() would reject them as being part of an Apply-Actions that
     * follows a Write-Actions, which is an invalid order.  */
    error = ofpacts_parse(arg, ofpacts, usable_protocols, false,
                          OFPACT_WRITE_ACTIONS);

    /* Put the Write-Actions back on and update its length. */
    on = ofpbuf_push_uninit(ofpacts, sizeof *on);
    on->ofpact.len = ofpacts->size;

    /* Put any previous actions or instructions back on. */
    ofpbuf_push_uninit(ofpacts, ofs);

    return error;
}