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