+ default:
+ return OFPERR_OFPBAC_BAD_TYPE;
+ }
+}
+
+static enum ofperr
+set_field_from_openflow(const struct ofp12_action_set_field *oasf,
+ struct ofpbuf *ofpacts)
+{
+ uint16_t oasf_len = ntohs(oasf->len);
+ uint32_t oxm_header = ntohl(oasf->dst);
+ uint8_t oxm_length = NXM_LENGTH(oxm_header);
+ struct ofpact_set_field *sf;
+ const struct mf_field *mf;
+
+ /* ofp12_action_set_field is padded to 64 bits by zero */
+ if (oasf_len != ROUND_UP(sizeof *oasf + oxm_length, 8)) {
+ return OFPERR_OFPBAC_BAD_SET_LEN;
+ }
+ if (!is_all_zeros((const uint8_t *)oasf + sizeof *oasf + oxm_length,
+ oasf_len - oxm_length - sizeof *oasf)) {
+ return OFPERR_OFPBAC_BAD_SET_ARGUMENT;
+ }
+
+ if (NXM_HASMASK(oxm_header)) {
+ return OFPERR_OFPBAC_BAD_SET_TYPE;
+ }
+ mf = mf_from_nxm_header(oxm_header);
+ if (!mf) {
+ return OFPERR_OFPBAC_BAD_SET_TYPE;
+ }
+ ovs_assert(mf->n_bytes == oxm_length);
+ /* oxm_length is now validated to be compatible with mf_value. */
+ if (!mf->writable) {
+ VLOG_WARN_RL(&rl, "destination field %s is not writable", mf->name);
+ return OFPERR_OFPBAC_BAD_SET_ARGUMENT;
+ }
+ sf = ofpact_put_SET_FIELD(ofpacts);
+ sf->field = mf;
+ memcpy(&sf->value, oasf + 1, mf->n_bytes);
+
+ /* The value must be valid for match and must have the OFPVID_PRESENT bit
+ * on for OXM_OF_VLAN_VID. */
+ if (!mf_is_value_valid(mf, &sf->value)
+ || (mf->id == MFF_VLAN_VID
+ && !(sf->value.be16 & htons(OFPVID12_PRESENT)))) {
+ struct ds ds = DS_EMPTY_INITIALIZER;
+ mf_format(mf, &sf->value, NULL, &ds);
+ VLOG_WARN_RL(&rl, "Invalid value for set field %s: %s",
+ mf->name, ds_cstr(&ds));
+ ds_destroy(&ds);
+
+ return OFPERR_OFPBAC_BAD_SET_ARGUMENT;
+ }
+ return 0;
+}
+
+static void
+set_field_to_openflow12(const struct ofpact_set_field *sf,
+ struct ofpbuf *openflow)
+{
+ uint16_t padded_value_len = ROUND_UP(sf->field->n_bytes, 8);
+ struct ofp12_action_set_field *oasf;
+ char *value;
+
+ oasf = ofputil_put_OFPAT12_SET_FIELD(openflow);
+ oasf->dst = htonl(sf->field->oxm_header);
+ oasf->len = htons(sizeof *oasf + padded_value_len);
+
+ value = ofpbuf_put_zeros(openflow, padded_value_len);
+ memcpy(value, &sf->value, sf->field->n_bytes);
+}
+
+/* Convert 'sf' to one or two REG_LOADs. */
+static void
+set_field_to_nxast(const struct ofpact_set_field *sf, struct ofpbuf *openflow)
+{
+ const struct mf_field *mf = sf->field;
+ struct nx_action_reg_load *narl;
+
+ if (mf->n_bits > 64) {
+ ovs_assert(mf->n_bytes == 16); /* IPv6 addr. */
+ /* Split into 64bit chunks */
+ /* Lower bits first. */
+ narl = ofputil_put_NXAST_REG_LOAD(openflow);
+ narl->ofs_nbits = nxm_encode_ofs_nbits(0, 64);
+ narl->dst = htonl(mf->nxm_header);
+ memcpy(&narl->value, &sf->value.ipv6.s6_addr[8], sizeof narl->value);
+ /* Higher bits next. */
+ narl = ofputil_put_NXAST_REG_LOAD(openflow);
+ narl->ofs_nbits = nxm_encode_ofs_nbits(64, mf->n_bits - 64);
+ narl->dst = htonl(mf->nxm_header);
+ memcpy(&narl->value, &sf->value.ipv6.s6_addr[0], sizeof narl->value);
+ } else {
+ narl = ofputil_put_NXAST_REG_LOAD(openflow);
+ narl->ofs_nbits = nxm_encode_ofs_nbits(0, mf->n_bits);
+ narl->dst = htonl(mf->nxm_header);
+ memset(&narl->value, 0, 8 - mf->n_bytes);
+ memcpy((char*)&narl->value + (8 - mf->n_bytes),
+ &sf->value, mf->n_bytes);
+ }
+}
+
+/* Convert 'sf' to standard OpenFlow 1.1 actions, if we can, falling back
+ * to Nicira extensions if we must.
+ *
+ * We check only meta-flow types that can appear within set field actions and
+ * that have a mapping to compatible action types. These struct mf_field
+ * definitions have a defined OXM or NXM header value and specify the field as
+ * writable. */
+static void
+set_field_to_openflow11(const struct ofpact_set_field *sf,
+ struct ofpbuf *openflow)
+{
+ switch ((int) sf->field->id) {
+ case MFF_VLAN_TCI:
+ /* NXM_OF_VLAN_TCI to OpenFlow 1.1 mapping:
+ *
+ * If CFI=1, Add or modify VLAN VID & PCP.
+ * OpenFlow 1.1 set actions only apply if the packet
+ * already has VLAN tags. To be sure that is the case
+ * we have to push a VLAN header. As we do not support
+ * multiple layers of VLANs, this is a no-op, if a VLAN
+ * header already exists. This may backfire, however,
+ * when we start supporting multiple layers of VLANs.
+ * If CFI=0, strip VLAN header, if any.
+ */
+ if (sf->value.be16 & htons(VLAN_CFI)) {
+ /* Push a VLAN tag, if one was not seen at action validation
+ * time. */
+ if (!sf->flow_has_vlan) {
+ ofputil_put_OFPAT11_PUSH_VLAN(openflow)->ethertype
+ = htons(ETH_TYPE_VLAN_8021Q);
+ }
+ ofputil_put_OFPAT11_SET_VLAN_VID(openflow)->vlan_vid
+ = sf->value.be16 & htons(VLAN_VID_MASK);
+ ofputil_put_OFPAT11_SET_VLAN_PCP(openflow)->vlan_pcp
+ = vlan_tci_to_pcp(sf->value.be16);
+ } else {
+ /* If the flow did not match on vlan, we have no way of
+ * knowing if the vlan tag exists, so we must POP just to be
+ * sure. */
+ ofputil_put_OFPAT11_POP_VLAN(openflow);
+ }
+ break;
+
+ case MFF_VLAN_VID:
+ /* OXM VLAN_PCP to OpenFlow 1.1.
+ * Set field on OXM_OF_VLAN_VID onlyapplies to an existing vlan
+ * tag. Clear the OFPVID_PRESENT bit.
+ */
+ ofputil_put_OFPAT11_SET_VLAN_VID(openflow)->vlan_vid
+ = sf->value.be16 & htons(VLAN_VID_MASK);
+ break;
+
+ case MFF_VLAN_PCP:
+ /* OXM VLAN_PCP to OpenFlow 1.1.
+ * OXM_OF_VLAN_PCP only applies to existing vlan tag. */
+ ofputil_put_OFPAT11_SET_VLAN_PCP(openflow)->vlan_pcp = sf->value.u8;
+ break;
+
+ case MFF_ETH_SRC:
+ memcpy(ofputil_put_OFPAT11_SET_DL_SRC(openflow)->dl_addr,
+ sf->value.mac, ETH_ADDR_LEN);
+ break;
+
+ case MFF_ETH_DST:
+ memcpy(ofputil_put_OFPAT11_SET_DL_DST(openflow)->dl_addr,
+ sf->value.mac, ETH_ADDR_LEN);
+ break;
+
+ case MFF_MPLS_LABEL:
+ ofputil_put_OFPAT11_SET_MPLS_LABEL(openflow)->mpls_label =
+ sf->value.be32;
+ break;
+
+ case MFF_MPLS_TC:
+ ofputil_put_OFPAT11_SET_MPLS_TC(openflow)->mpls_tc = sf->value.u8;
+ break;
+
+ case MFF_IPV4_SRC:
+ ofputil_put_OFPAT11_SET_NW_SRC(openflow)->nw_addr = sf->value.be32;
+ break;
+
+ case MFF_IPV4_DST:
+ ofputil_put_OFPAT11_SET_NW_DST(openflow)->nw_addr = sf->value.be32;
+ break;
+
+ case MFF_IP_DSCP:
+ ofputil_put_OFPAT11_SET_NW_TOS(openflow)->nw_tos = sf->value.u8;
+ break;
+
+ case MFF_IP_DSCP_SHIFTED:
+ ofputil_put_OFPAT11_SET_NW_TOS(openflow)->nw_tos = sf->value.u8 << 2;
+ break;
+
+ case MFF_IP_ECN:
+ ofputil_put_OFPAT11_SET_NW_ECN(openflow)->nw_ecn = sf->value.u8;
+ break;
+
+ case MFF_IP_TTL:
+ ofputil_put_OFPAT11_SET_NW_TTL(openflow)->nw_ttl = sf->value.u8;
+ break;
+
+ case MFF_TCP_SRC:
+ case MFF_UDP_SRC:
+ case MFF_SCTP_SRC:
+ ofputil_put_OFPAT11_SET_TP_SRC(openflow)->tp_port = sf->value.be16;
+ break;
+
+ case MFF_TCP_DST:
+ case MFF_UDP_DST:
+ case MFF_SCTP_DST:
+ ofputil_put_OFPAT11_SET_TP_DST(openflow)->tp_port = sf->value.be16;
+ break;
+
+ default:
+ set_field_to_nxast(sf, openflow);
+ break;
+ }
+}
+
+/* Convert 'sf' to standard OpenFlow 1.0 actions, if we can, falling back
+ * to Nicira extensions if we must.
+ *
+ * We check only meta-flow types that can appear within set field actions and
+ * that have a mapping to compatible action types. These struct mf_field
+ * definitions have a defined OXM or NXM header value and specify the field as
+ * writable. */
+static void
+set_field_to_openflow10(const struct ofpact_set_field *sf,
+ struct ofpbuf *openflow)
+{
+ switch ((int) sf->field->id) {
+ case MFF_VLAN_TCI:
+ /* NXM_OF_VLAN_TCI to OpenFlow 1.0 mapping:
+ *
+ * If CFI=1, Add or modify VLAN VID & PCP.
+ * If CFI=0, strip VLAN header, if any.
+ */
+ if (sf->value.be16 & htons(VLAN_CFI)) {
+ ofputil_put_OFPAT10_SET_VLAN_VID(openflow)->vlan_vid
+ = sf->value.be16 & htons(VLAN_VID_MASK);
+ ofputil_put_OFPAT10_SET_VLAN_PCP(openflow)->vlan_pcp
+ = vlan_tci_to_pcp(sf->value.be16);
+ } else {
+ ofputil_put_OFPAT10_STRIP_VLAN(openflow);
+ }
+ break;
+
+ case MFF_VLAN_VID:
+ /* OXM VLAN_VID to OpenFlow 1.0.
+ * Set field on OXM_OF_VLAN_VID onlyapplies to an existing vlan
+ * tag. Clear the OFPVID_PRESENT bit.
+ */
+ ofputil_put_OFPAT10_SET_VLAN_VID(openflow)->vlan_vid
+ = sf->value.be16 & htons(VLAN_VID_MASK);
+ break;
+
+ case MFF_VLAN_PCP:
+ /* OXM VLAN_PCP to OpenFlow 1.0.
+ * OXM_OF_VLAN_PCP only applies to existing vlan tag. */
+ ofputil_put_OFPAT10_SET_VLAN_PCP(openflow)->vlan_pcp = sf->value.u8;
+ break;
+
+ case MFF_ETH_SRC:
+ memcpy(ofputil_put_OFPAT10_SET_DL_SRC(openflow)->dl_addr,
+ sf->value.mac, ETH_ADDR_LEN);
+ break;
+
+ case MFF_ETH_DST:
+ memcpy(ofputil_put_OFPAT10_SET_DL_DST(openflow)->dl_addr,
+ sf->value.mac, ETH_ADDR_LEN);
+ break;
+
+ case MFF_IPV4_SRC:
+ ofputil_put_OFPAT10_SET_NW_SRC(openflow)->nw_addr = sf->value.be32;
+ break;
+
+ case MFF_IPV4_DST:
+ ofputil_put_OFPAT10_SET_NW_DST(openflow)->nw_addr = sf->value.be32;
+ break;
+
+ case MFF_IP_DSCP:
+ ofputil_put_OFPAT10_SET_NW_TOS(openflow)->nw_tos = sf->value.u8;
+ break;
+
+ case MFF_IP_DSCP_SHIFTED:
+ ofputil_put_OFPAT10_SET_NW_TOS(openflow)->nw_tos = sf->value.u8 << 2;
+ break;
+
+ case MFF_TCP_SRC:
+ case MFF_UDP_SRC:
+ ofputil_put_OFPAT10_SET_TP_SRC(openflow)->tp_port = sf->value.be16;
+ break;
+
+ case MFF_TCP_DST:
+ case MFF_UDP_DST:
+ ofputil_put_OFPAT10_SET_TP_DST(openflow)->tp_port = sf->value.be16;
+ break;
+
+ default:
+ set_field_to_nxast(sf, openflow);
+ break;
+ }
+}
+
+static void
+set_field_to_openflow(const struct ofpact_set_field *sf,
+ struct ofpbuf *openflow)
+{
+ struct ofp_header *oh = (struct ofp_header *)openflow->frame;
+
+ if (oh->version >= OFP12_VERSION) {
+ set_field_to_openflow12(sf, openflow);
+ } else if (oh->version == OFP11_VERSION) {
+ set_field_to_openflow11(sf, openflow);
+ } else if (oh->version == OFP10_VERSION) {
+ set_field_to_openflow10(sf, openflow);
+ } else {
+ OVS_NOT_REACHED();
+ }
+}
+
+static enum ofperr
+output_from_openflow11(const struct ofp11_action_output *oao,
+ struct ofpbuf *out)
+{
+ struct ofpact_output *output;
+ enum ofperr error;
+
+ output = ofpact_put_OUTPUT(out);
+ output->max_len = ntohs(oao->max_len);
+
+ error = ofputil_port_from_ofp11(oao->port, &output->port);
+ if (error) {
+ return error;
+ }
+
+ return ofpact_check_output_port(output->port, OFPP_MAX);
+}
+
+static enum ofperr
+ofpact_from_openflow11(const union ofp_action *a, enum ofp_version version,
+ struct ofpbuf *out)
+{
+ enum ofputil_action_code code;
+ enum ofperr error;
+ struct ofpact_vlan_vid *vlan_vid;
+ struct ofpact_vlan_pcp *vlan_pcp;
+
+ error = decode_openflow11_action(a, &code);
+ if (error) {
+ return error;
+ }
+
+ if (version >= OFP12_VERSION) {
+ switch ((int)code) {
+ case OFPUTIL_OFPAT11_SET_VLAN_VID:
+ case OFPUTIL_OFPAT11_SET_VLAN_PCP:
+ case OFPUTIL_OFPAT11_SET_DL_SRC:
+ case OFPUTIL_OFPAT11_SET_DL_DST:
+ case OFPUTIL_OFPAT11_SET_NW_SRC:
+ case OFPUTIL_OFPAT11_SET_NW_DST:
+ case OFPUTIL_OFPAT11_SET_NW_TOS:
+ case OFPUTIL_OFPAT11_SET_NW_ECN:
+ case OFPUTIL_OFPAT11_SET_TP_SRC:
+ case OFPUTIL_OFPAT11_SET_TP_DST:
+ VLOG_WARN_RL(&rl, "Deprecated action %s received over %s",
+ ofputil_action_name_from_code(code),
+ ofputil_version_to_string(version));
+ }
+ }
+
+ switch (code) {
+ case OFPUTIL_ACTION_INVALID:
+#define OFPAT10_ACTION(ENUM, STRUCT, NAME) case OFPUTIL_##ENUM:
+#define OFPAT13_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) case OFPUTIL_##ENUM:
+#include "ofp-util.def"
+ OVS_NOT_REACHED();
+
+ case OFPUTIL_OFPAT11_OUTPUT:
+ return output_from_openflow11(&a->ofp11_output, out);
+
+ case OFPUTIL_OFPAT11_SET_VLAN_VID:
+ if (a->vlan_vid.vlan_vid & ~htons(0xfff)) {
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
+ }
+ vlan_vid = ofpact_put_SET_VLAN_VID(out);
+ vlan_vid->vlan_vid = ntohs(a->vlan_vid.vlan_vid);
+ vlan_vid->push_vlan_if_needed = false;
+ vlan_vid->ofpact.compat = code;
+ break;
+
+ case OFPUTIL_OFPAT11_SET_VLAN_PCP:
+ if (a->vlan_pcp.vlan_pcp & ~7) {
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
+ }
+ vlan_pcp = ofpact_put_SET_VLAN_PCP(out);
+ vlan_pcp->vlan_pcp = a->vlan_pcp.vlan_pcp;
+ vlan_pcp->push_vlan_if_needed = false;
+ vlan_pcp->ofpact.compat = code;
+ break;
+
+ case OFPUTIL_OFPAT11_PUSH_VLAN:
+ if (a->push.ethertype != htons(ETH_TYPE_VLAN_8021Q)) {
+ /* XXX 802.1AD(QinQ) isn't supported at the moment */
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
+ }
+ ofpact_put_PUSH_VLAN(out);
+ break;
+
+ case OFPUTIL_OFPAT11_POP_VLAN:
+ ofpact_put_STRIP_VLAN(out)->ofpact.compat = code;
+ break;
+
+ case OFPUTIL_OFPAT11_SET_QUEUE:
+ ofpact_put_SET_QUEUE(out)->queue_id =
+ ntohl(a->ofp11_set_queue.queue_id);
+ break;
+
+ case OFPUTIL_OFPAT11_SET_DL_SRC:
+ memcpy(ofpact_put_SET_ETH_SRC(out)->mac, a->dl_addr.dl_addr,
+ ETH_ADDR_LEN);
+ break;
+
+ case OFPUTIL_OFPAT11_SET_DL_DST:
+ memcpy(ofpact_put_SET_ETH_DST(out)->mac, a->dl_addr.dl_addr,
+ ETH_ADDR_LEN);
+ break;
+
+ case OFPUTIL_OFPAT11_DEC_NW_TTL:
+ dec_ttl_from_openflow(out, code);
+ break;
+
+ case OFPUTIL_OFPAT11_SET_NW_SRC:
+ ofpact_put_SET_IPV4_SRC(out)->ipv4 = a->nw_addr.nw_addr;
+ break;
+
+ case OFPUTIL_OFPAT11_SET_NW_DST:
+ ofpact_put_SET_IPV4_DST(out)->ipv4 = a->nw_addr.nw_addr;
+ break;
+
+ case OFPUTIL_OFPAT11_SET_NW_TOS:
+ if (a->nw_tos.nw_tos & ~IP_DSCP_MASK) {
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
+ }
+ ofpact_put_SET_IP_DSCP(out)->dscp = a->nw_tos.nw_tos;
+ break;
+
+ case OFPUTIL_OFPAT11_SET_NW_ECN:
+ if (a->nw_ecn.nw_ecn & ~IP_ECN_MASK) {
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
+ }
+ ofpact_put_SET_IP_ECN(out)->ecn = a->nw_ecn.nw_ecn;
+ break;
+
+ case OFPUTIL_OFPAT11_SET_NW_TTL:
+ ofpact_put_SET_IP_TTL(out)->ttl = a->nw_ttl.nw_ttl;
+ break;
+
+ case OFPUTIL_OFPAT11_SET_TP_SRC:
+ ofpact_put_SET_L4_SRC_PORT(out)->port = ntohs(a->tp_port.tp_port);
+ break;
+
+ case OFPUTIL_OFPAT11_SET_TP_DST:
+ ofpact_put_SET_L4_DST_PORT(out)->port = ntohs(a->tp_port.tp_port);
+ break;
+
+ case OFPUTIL_OFPAT12_SET_FIELD:
+ return set_field_from_openflow(&a->set_field, out);
+
+ case OFPUTIL_OFPAT11_SET_MPLS_LABEL:
+ ofpact_put_SET_MPLS_LABEL(out)->label = a->ofp11_mpls_label.mpls_label;
+ break;
+
+ case OFPUTIL_OFPAT11_SET_MPLS_TC:
+ ofpact_put_SET_MPLS_TC(out)->tc = a->ofp11_mpls_tc.mpls_tc;
+ break;
+
+ case OFPUTIL_OFPAT11_SET_MPLS_TTL:
+ ofpact_put_SET_MPLS_TTL(out)->ttl = a->ofp11_mpls_ttl.mpls_ttl;
+ break;
+
+ case OFPUTIL_OFPAT11_DEC_MPLS_TTL:
+ ofpact_put_DEC_MPLS_TTL(out);
+ break;
+
+ case OFPUTIL_OFPAT11_PUSH_MPLS:
+ error = push_mpls_from_openflow(a->push.ethertype, out);
+ break;
+
+ case OFPUTIL_OFPAT11_POP_MPLS:
+ if (eth_type_mpls(a->ofp11_pop_mpls.ethertype)) {
+ return OFPERR_OFPBAC_BAD_ARGUMENT;
+ }
+ ofpact_put_POP_MPLS(out)->ethertype = a->ofp11_pop_mpls.ethertype;
+ break;
+
+ case OFPUTIL_OFPAT11_GROUP:
+ ofpact_put_GROUP(out)->group_id = ntohl(a->group.group_id);
+ break;
+
+#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) case OFPUTIL_##ENUM:
+#include "ofp-util.def"
+ return ofpact_from_nxast(a, code, out);
+ }
+
+ return error;
+}
+
+/* True if an action sets the value of a field
+ * in a way that is compatibile with the action set.
+ * False otherwise. */
+static bool
+ofpact_is_set_action(const struct ofpact *a)
+{
+ switch (a->type) {
+ case OFPACT_SET_FIELD:
+ case OFPACT_REG_LOAD:
+ case OFPACT_SET_ETH_DST:
+ case OFPACT_SET_ETH_SRC:
+ case OFPACT_SET_IP_DSCP:
+ case OFPACT_SET_IP_ECN:
+ case OFPACT_SET_IP_TTL:
+ case OFPACT_SET_IPV4_DST:
+ case OFPACT_SET_IPV4_SRC:
+ case OFPACT_SET_L4_DST_PORT:
+ case OFPACT_SET_L4_SRC_PORT:
+ case OFPACT_SET_MPLS_LABEL:
+ case OFPACT_SET_MPLS_TC:
+ case OFPACT_SET_MPLS_TTL:
+ case OFPACT_SET_QUEUE:
+ case OFPACT_SET_TUNNEL:
+ case OFPACT_SET_VLAN_PCP:
+ case OFPACT_SET_VLAN_VID:
+ return true;
+ case OFPACT_BUNDLE:
+ case OFPACT_CLEAR_ACTIONS:
+ case OFPACT_CONTROLLER:
+ case OFPACT_DEC_MPLS_TTL:
+ case OFPACT_DEC_TTL:
+ case OFPACT_ENQUEUE:
+ case OFPACT_EXIT:
+ case OFPACT_FIN_TIMEOUT:
+ case OFPACT_GOTO_TABLE:
+ case OFPACT_GROUP:
+ case OFPACT_LEARN:
+ case OFPACT_METER:
+ case OFPACT_MULTIPATH:
+ case OFPACT_NOTE:
+ case OFPACT_OUTPUT:
+ case OFPACT_OUTPUT_REG:
+ case OFPACT_POP_MPLS:
+ case OFPACT_POP_QUEUE:
+ case OFPACT_PUSH_MPLS:
+ case OFPACT_PUSH_VLAN:
+ case OFPACT_REG_MOVE:
+ case OFPACT_RESUBMIT:
+ case OFPACT_SAMPLE:
+ case OFPACT_STACK_POP:
+ case OFPACT_STACK_PUSH:
+ case OFPACT_STRIP_VLAN:
+ case OFPACT_WRITE_ACTIONS:
+ case OFPACT_WRITE_METADATA:
+ return false;
+ default:
+ OVS_NOT_REACHED();
+ }
+}
+
+/* True if an action is allowed in the action set.
+ * False otherwise. */
+static bool
+ofpact_is_allowed_in_actions_set(const struct ofpact *a)
+{
+ switch (a->type) {
+ case OFPACT_DEC_MPLS_TTL:
+ case OFPACT_DEC_TTL:
+ case OFPACT_GROUP:
+ case OFPACT_OUTPUT:
+ case OFPACT_POP_MPLS:
+ case OFPACT_PUSH_MPLS:
+ case OFPACT_PUSH_VLAN:
+ case OFPACT_REG_LOAD:
+ case OFPACT_SET_FIELD:
+ case OFPACT_SET_ETH_DST:
+ case OFPACT_SET_ETH_SRC:
+ case OFPACT_SET_IP_DSCP:
+ case OFPACT_SET_IP_ECN:
+ case OFPACT_SET_IP_TTL:
+ case OFPACT_SET_IPV4_DST:
+ case OFPACT_SET_IPV4_SRC:
+ case OFPACT_SET_L4_DST_PORT:
+ case OFPACT_SET_L4_SRC_PORT:
+ case OFPACT_SET_MPLS_LABEL:
+ case OFPACT_SET_MPLS_TC:
+ case OFPACT_SET_MPLS_TTL:
+ case OFPACT_SET_QUEUE:
+ case OFPACT_SET_TUNNEL:
+ case OFPACT_SET_VLAN_PCP:
+ case OFPACT_SET_VLAN_VID:
+ case OFPACT_STRIP_VLAN:
+ return true;
+
+ /* In general these actions are excluded because they are not part of
+ * the OpenFlow specification nor map to actions that are defined in
+ * the specification. Thus the order in which they should be applied
+ * in the action set is undefined. */
+ case OFPACT_BUNDLE:
+ case OFPACT_CONTROLLER:
+ case OFPACT_ENQUEUE:
+ case OFPACT_EXIT:
+ case OFPACT_FIN_TIMEOUT:
+ case OFPACT_LEARN:
+ case OFPACT_MULTIPATH:
+ case OFPACT_NOTE:
+ case OFPACT_OUTPUT_REG:
+ case OFPACT_POP_QUEUE:
+ case OFPACT_REG_MOVE:
+ case OFPACT_RESUBMIT:
+ case OFPACT_SAMPLE:
+ case OFPACT_STACK_POP:
+ case OFPACT_STACK_PUSH:
+
+ /* The action set may only include actions and thus
+ * may not include any instructions */
+ case OFPACT_CLEAR_ACTIONS:
+ case OFPACT_GOTO_TABLE:
+ case OFPACT_METER:
+ case OFPACT_WRITE_ACTIONS:
+ case OFPACT_WRITE_METADATA:
+ return false;
+ default:
+ OVS_NOT_REACHED();
+ }
+}
+
+/* Append ofpact 'a' onto the tail of 'out' */
+static void
+ofpact_copy(struct ofpbuf *out, const struct ofpact *a)
+{
+ ofpbuf_put(out, a, OFPACT_ALIGN(a->len));
+}
+
+/* Copies the last ofpact whose type is 'filter' from 'in' to 'out'. */
+static bool
+ofpacts_copy_last(struct ofpbuf *out, const struct ofpbuf *in,
+ enum ofpact_type filter)
+{
+ const struct ofpact *target;
+ const struct ofpact *a;
+
+ target = NULL;
+ OFPACT_FOR_EACH (a, ofpbuf_data(in), ofpbuf_size(in)) {
+ if (a->type == filter) {
+ target = a;
+ }
+ }
+ if (target) {
+ ofpact_copy(out, target);
+ }
+ return target != NULL;
+}
+
+/* Append all ofpacts, for which 'filter' returns true, from 'in' to 'out'.
+ * The order of appended ofpacts is preserved between 'in' and 'out' */
+static void
+ofpacts_copy_all(struct ofpbuf *out, const struct ofpbuf *in,
+ bool (*filter)(const struct ofpact *))
+{
+ const struct ofpact *a;
+
+ OFPACT_FOR_EACH (a, ofpbuf_data(in), ofpbuf_size(in)) {
+ if (filter(a)) {
+ ofpact_copy(out, a);
+ }
+ }
+}
+
+/* Reads 'action_set', which contains ofpacts accumulated by
+ * OFPACT_WRITE_ACTIONS instructions, and writes equivalent actions to be
+ * executed directly into 'action_list'. (These names correspond to the
+ * "Action Set" and "Action List" terms used in OpenFlow 1.1+.)
+ *
+ * In general this involves appending the last instance of each action that is
+ * adimissible in the action set in the order described in the OpenFlow
+ * specification.
+ *
+ * Exceptions:
+ * + output action is only appended if no group action was present in 'in'.
+ * + As a simplification all set actions are copied in the order the are
+ * provided in 'in' as many set actions applied to a field has the same
+ * affect as only applying the last action that sets a field and
+ * duplicates are removed by do_xlate_actions().
+ * This has an unwanted side-effect of compsoting multiple
+ * LOAD_REG actions that touch different regions of the same field. */
+void
+ofpacts_execute_action_set(struct ofpbuf *action_list,
+ const struct ofpbuf *action_set)
+{
+ /* The OpenFlow spec "Action Set" section specifies this order. */
+ ofpacts_copy_last(action_list, action_set, OFPACT_STRIP_VLAN);
+ ofpacts_copy_last(action_list, action_set, OFPACT_POP_MPLS);
+ ofpacts_copy_last(action_list, action_set, OFPACT_PUSH_MPLS);
+ ofpacts_copy_last(action_list, action_set, OFPACT_PUSH_VLAN);
+ ofpacts_copy_last(action_list, action_set, OFPACT_DEC_TTL);
+ ofpacts_copy_last(action_list, action_set, OFPACT_DEC_MPLS_TTL);
+ ofpacts_copy_all(action_list, action_set, ofpact_is_set_action);
+ ofpacts_copy_last(action_list, action_set, OFPACT_SET_QUEUE);
+
+ /* If both OFPACT_GROUP and OFPACT_OUTPUT are present, OpenFlow says that
+ * we should execute only OFPACT_GROUP.
+ *
+ * If neither OFPACT_GROUP nor OFPACT_OUTPUT is present, then we can drop
+ * all the actions because there's no point in modifying a packet that will
+ * not be sent anywhere. */
+ if (!ofpacts_copy_last(action_list, action_set, OFPACT_GROUP) &&
+ !ofpacts_copy_last(action_list, action_set, OFPACT_OUTPUT)) {
+ ofpbuf_clear(action_list);
+ }
+}
+
+
+static enum ofperr
+ofpacts_from_openflow11_for_action_set(const union ofp_action *in,
+ size_t n_in, enum ofp_version version,
+ struct ofpbuf *out)
+{
+ enum ofperr error;
+ struct ofpact *a;
+ size_t start = ofpbuf_size(out);
+
+ error = ofpacts_from_openflow(in, n_in, version, out);
+
+ if (error) {
+ return error;
+ }
+
+ OFPACT_FOR_EACH (a, ofpact_end(ofpbuf_data(out), start), ofpbuf_size(out) - start) {
+ if (!ofpact_is_allowed_in_actions_set(a)) {
+ VLOG_WARN_RL(&rl, "disallowed action in action set");
+ return OFPERR_OFPBAC_BAD_TYPE;
+ }
+ }
+
+ return 0;
+}
+
+\f
+/* OpenFlow 1.1 instructions. */
+
+#define DEFINE_INST(ENUM, STRUCT, EXTENSIBLE, NAME) \
+ static inline const struct STRUCT * OVS_UNUSED \
+ instruction_get_##ENUM(const struct ofp11_instruction *inst)\
+ { \
+ ovs_assert(inst->type == htons(ENUM)); \
+ return ALIGNED_CAST(struct STRUCT *, inst); \
+ } \
+ \
+ static inline void OVS_UNUSED \
+ instruction_init_##ENUM(struct STRUCT *s) \
+ { \
+ memset(s, 0, sizeof *s); \
+ s->type = htons(ENUM); \
+ s->len = htons(sizeof *s); \
+ } \
+ \
+ static inline struct STRUCT * OVS_UNUSED \
+ instruction_put_##ENUM(struct ofpbuf *buf) \
+ { \
+ struct STRUCT *s = ofpbuf_put_uninit(buf, sizeof *s); \
+ instruction_init_##ENUM(s); \
+ return s; \
+ }
+OVS_INSTRUCTIONS
+#undef DEFINE_INST
+
+struct instruction_type_info {
+ enum ovs_instruction_type type;
+ const char *name;
+};
+
+static const struct instruction_type_info inst_info[] = {
+#define DEFINE_INST(ENUM, STRUCT, EXTENSIBLE, NAME) {OVSINST_##ENUM, NAME},
+OVS_INSTRUCTIONS
+#undef DEFINE_INST
+};
+
+const char *
+ovs_instruction_name_from_type(enum ovs_instruction_type type)
+{
+ return inst_info[type].name;
+}
+
+int
+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;
+}
+
+enum ovs_instruction_type
+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_CONTROLLER:
+ case OFPACT_ENQUEUE:
+ case OFPACT_OUTPUT_REG:
+ 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_REG_LOAD:
+ 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_MULTIPATH:
+ case OFPACT_NOTE:
+ case OFPACT_EXIT:
+ case OFPACT_SAMPLE:
+ default:
+ return OVSINST_OFPIT11_APPLY_ACTIONS;
+ }
+}
+
+enum ofperr
+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;
+ }
+}
+
+static inline struct ofp11_instruction *
+instruction_next(const struct ofp11_instruction *inst)
+{
+ return ((struct ofp11_instruction *) (void *)
+ ((uint8_t *) inst + ntohs(inst->len)));
+}
+
+static inline bool
+instruction_is_valid(const struct ofp11_instruction *inst,
+ size_t n_instructions)
+{
+ uint16_t len = ntohs(inst->len);
+ return (!(len % OFP11_INSTRUCTION_ALIGN)
+ && len >= sizeof *inst
+ && len / sizeof *inst <= n_instructions);
+}
+
+/* This macro is careful to check for instructions with bad lengths. */
+#define INSTRUCTION_FOR_EACH(ITER, LEFT, INSTRUCTIONS, N_INSTRUCTIONS) \
+ for ((ITER) = (INSTRUCTIONS), (LEFT) = (N_INSTRUCTIONS); \
+ (LEFT) > 0 && instruction_is_valid(ITER, LEFT); \
+ ((LEFT) -= (ntohs((ITER)->len) \
+ / sizeof(struct ofp11_instruction)), \
+ (ITER) = instruction_next(ITER)))
+
+static enum ofperr
+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;
+ }
+}
+
+static enum ofperr
+decode_openflow11_instructions(const struct ofp11_instruction insts[],
+ size_t n_insts,
+ const struct ofp11_instruction *out[])
+{
+ const struct ofp11_instruction *inst;
+ size_t left;
+
+ memset(out, 0, N_OVS_INSTRUCTIONS * sizeof *out);
+ INSTRUCTION_FOR_EACH (inst, left, insts, n_insts) {
+ enum ovs_instruction_type type;
+ enum ofperr error;
+
+ error = decode_openflow11_instruction(inst, &type);
+ if (error) {
+ return error;
+ }
+
+ if (out[type]) {
+ return OFPERR_ONFBIC_DUP_INSTRUCTION;
+ }
+ out[type] = inst;
+ }
+
+ if (left) {
+ VLOG_WARN_RL(&rl, "bad instruction format at offset %"PRIuSIZE,
+ (n_insts - left) * sizeof *inst);
+ return OFPERR_OFPBIC_BAD_LEN;
+ }
+ return 0;
+}
+
+static void
+get_actions_from_instruction(const struct ofp11_instruction *inst,
+ const union ofp_action **actions,
+ size_t *max_actions)
+{
+ *actions = ALIGNED_CAST(const union ofp_action *, inst + 1);
+ *max_actions = (ntohs(inst->len) - sizeof *inst) / OFP11_INSTRUCTION_ALIGN;
+}
+
+enum ofperr
+ofpacts_pull_openflow_instructions(struct ofpbuf *openflow,
+ unsigned int instructions_len,
+ enum ofp_version version,
+ struct ofpbuf *ofpacts)
+{
+ const struct ofp11_instruction *instructions;
+ const struct ofp11_instruction *insts[N_OVS_INSTRUCTIONS];
+ enum ofperr error;
+
+ ofpbuf_clear(ofpacts);
+
+ if (instructions_len % OFP11_INSTRUCTION_ALIGN != 0) {
+ VLOG_WARN_RL(&rl, "OpenFlow message instructions length %u is not a "
+ "multiple of %d",
+ instructions_len, OFP11_INSTRUCTION_ALIGN);
+ error = OFPERR_OFPBIC_BAD_LEN;
+ goto exit;
+ }
+
+ instructions = ofpbuf_try_pull(openflow, instructions_len);
+ if (instructions == NULL) {
+ VLOG_WARN_RL(&rl, "OpenFlow message instructions length %u exceeds "
+ "remaining message length (%"PRIu32")",
+ instructions_len, ofpbuf_size(openflow));
+ error = OFPERR_OFPBIC_BAD_LEN;
+ goto exit;
+ }
+
+ error = decode_openflow11_instructions(
+ instructions, instructions_len / OFP11_INSTRUCTION_ALIGN,
+ insts);
+ if (error) {
+ goto exit;
+ }
+
+ if (insts[OVSINST_OFPIT13_METER]) {
+ const struct ofp13_instruction_meter *oim;
+ struct ofpact_meter *om;
+
+ oim = ALIGNED_CAST(const struct ofp13_instruction_meter *,
+ insts[OVSINST_OFPIT13_METER]);
+
+ om = ofpact_put_METER(ofpacts);
+ om->meter_id = ntohl(oim->meter_id);
+ }
+ if (insts[OVSINST_OFPIT11_APPLY_ACTIONS]) {
+ const union ofp_action *actions;
+ size_t max_actions;
+
+ get_actions_from_instruction(insts[OVSINST_OFPIT11_APPLY_ACTIONS],
+ &actions, &max_actions);
+ error = ofpacts_from_openflow(actions, max_actions, version, ofpacts);
+ if (error) {
+ goto exit;
+ }
+ }
+ if (insts[OVSINST_OFPIT11_CLEAR_ACTIONS]) {
+ instruction_get_OFPIT11_CLEAR_ACTIONS(
+ insts[OVSINST_OFPIT11_CLEAR_ACTIONS]);
+ ofpact_put_CLEAR_ACTIONS(ofpacts);
+ }
+ if (insts[OVSINST_OFPIT11_WRITE_ACTIONS]) {
+ struct ofpact_nest *on;
+ const union ofp_action *actions;
+ size_t max_actions;
+ size_t start;
+
+ ofpact_pad(ofpacts);
+ start = ofpbuf_size(ofpacts);
+ on = ofpact_put(ofpacts, OFPACT_WRITE_ACTIONS,
+ offsetof(struct ofpact_nest, actions));
+ get_actions_from_instruction(insts[OVSINST_OFPIT11_WRITE_ACTIONS],
+ &actions, &max_actions);
+ error = ofpacts_from_openflow11_for_action_set(actions, max_actions,
+ version, ofpacts);
+ if (error) {
+ goto exit;
+ }
+ on = ofpbuf_at_assert(ofpacts, start, sizeof *on);
+ on->ofpact.len = ofpbuf_size(ofpacts) - start;
+ }
+ if (insts[OVSINST_OFPIT11_WRITE_METADATA]) {
+ const struct ofp11_instruction_write_metadata *oiwm;
+ struct ofpact_metadata *om;
+
+ oiwm = ALIGNED_CAST(const struct ofp11_instruction_write_metadata *,
+ insts[OVSINST_OFPIT11_WRITE_METADATA]);
+
+ om = ofpact_put_WRITE_METADATA(ofpacts);
+ om->metadata = oiwm->metadata;
+ om->mask = oiwm->metadata_mask;
+ }
+ if (insts[OVSINST_OFPIT11_GOTO_TABLE]) {
+ const struct ofp11_instruction_goto_table *oigt;
+ struct ofpact_goto_table *ogt;
+
+ oigt = instruction_get_OFPIT11_GOTO_TABLE(
+ insts[OVSINST_OFPIT11_GOTO_TABLE]);
+ ogt = ofpact_put_GOTO_TABLE(ofpacts);
+ ogt->table_id = oigt->table_id;
+ }
+
+ error = ofpacts_verify(ofpbuf_data(ofpacts), ofpbuf_size(ofpacts));
+exit:
+ if (error) {
+ ofpbuf_clear(ofpacts);
+ }
+ return error;
+}
+\f
+/* Checks that 'port' is a valid output port for OFPACT_OUTPUT, given that the
+ * switch will never have more than 'max_ports' ports. Returns 0 if 'port' is
+ * valid, otherwise an OpenFlow error code. */
+enum ofperr
+ofpact_check_output_port(ofp_port_t port, ofp_port_t max_ports)
+{
+ switch (port) {
+ case OFPP_IN_PORT:
+ case OFPP_TABLE:
+ case OFPP_NORMAL:
+ case OFPP_FLOOD:
+ case OFPP_ALL:
+ case OFPP_CONTROLLER:
+ case OFPP_NONE:
+ case OFPP_LOCAL:
+ return 0;
+
+ default:
+ if (ofp_to_u16(port) < ofp_to_u16(max_ports)) {
+ return 0;
+ }
+ return OFPERR_OFPBAC_BAD_OUT_PORT;
+ }
+}
+
+/* Removes the protocols that require consistency between match and actions
+ * (that's everything but OpenFlow 1.0) from '*usable_protocols'.
+ *
+ * (An example of an inconsistency between match and actions is a flow that
+ * does not match on an MPLS Ethertype but has an action that pops an MPLS
+ * label.) */
+static void
+inconsistent_match(enum ofputil_protocol *usable_protocols)
+{
+ *usable_protocols &= OFPUTIL_P_OF10_ANY;
+}
+
+/* May modify flow->dl_type, flow->nw_proto and flow->vlan_tci,
+ * caller must restore them.
+ *
+ * Modifies some actions, filling in fields that could not be properly set
+ * without context. */
+static enum ofperr
+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;