mf_format_subfield(&move->dst, s);
}
-static void
-set_field_format(const struct ofpact_reg_load *load, struct ds *s)
-{
- const struct mf_field *mf = load->dst.field;
- union mf_value value;
-
- ovs_assert(load->ofpact.compat == OFPUTIL_OFPAT12_SET_FIELD);
- ds_put_format(s, "set_field:");
- memset(&value, 0, sizeof value);
- bitwise_copy(&load->subvalue, sizeof load->subvalue, 0,
- &value, mf->n_bytes, 0, load->dst.n_bits);
- mf_format(mf, &value, NULL, s);
- ds_put_format(s, "->%s", mf->name);
-}
-
-static void
-load_format(const struct ofpact_reg_load *load, struct ds *s)
+void
+nxm_format_reg_load(const struct ofpact_reg_load *load, struct ds *s)
{
ds_put_cstr(s, "load:");
mf_format_subvalue(&load->subvalue, s);
ds_put_cstr(s, "->");
mf_format_subfield(&load->dst, s);
}
-
-void
-nxm_format_reg_load(const struct ofpact_reg_load *load, struct ds *s)
-{
- if (load->ofpact.compat == OFPUTIL_OFPAT12_SET_FIELD) {
- set_field_format(load, s);
- } else {
- load_format(load, s);
- }
-}
\f
enum ofperr
nxm_reg_move_from_openflow(const struct nx_action_reg_move *narm,
return nxm_reg_load_check(load, NULL);
}
-
-enum ofperr
-nxm_reg_load_from_openflow12_set_field(
- 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_reg_load *load;
- 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;
- }
- load = ofpact_put_REG_LOAD(ofpacts);
- ofpact_set_field_init(load, mf, oasf + 1);
-
- return nxm_reg_load_check(load, NULL);
-}
\f
enum ofperr
nxm_reg_move_check(const struct ofpact_reg_move *move, const struct flow *flow)
narm->dst = htonl(move->dst.field->nxm_header);
}
-static void
-reg_load_to_nxast(const struct ofpact_reg_load *load, struct ofpbuf *openflow)
+void
+nxm_reg_load_to_nxast(const struct ofpact_reg_load *load,
+ struct ofpbuf *openflow)
{
struct nx_action_reg_load *narl;
narl->dst = htonl(load->dst.field->nxm_header);
narl->value = load->subvalue.be64[1];
}
-
-static void
-set_field_to_ofast(const struct ofpact_reg_load *load,
- struct ofpbuf *openflow)
-{
- const struct mf_field *mf = load->dst.field;
- uint16_t padded_value_len = ROUND_UP(mf->n_bytes, 8);
- struct ofp12_action_set_field *oasf;
- char *value;
-
- /* Set field is the only action of variable length (so far),
- * so handling the variable length portion is open-coded here */
- oasf = ofputil_put_OFPAT12_SET_FIELD(openflow);
- oasf->dst = htonl(mf->oxm_header);
- oasf->len = htons(ntohs(oasf->len) + padded_value_len);
-
- value = ofpbuf_put_zeros(openflow, padded_value_len);
- bitwise_copy(&load->subvalue, sizeof load->subvalue, load->dst.ofs,
- value, mf->n_bytes, load->dst.ofs, load->dst.n_bits);
-}
-
-void
-nxm_reg_load_to_nxast(const struct ofpact_reg_load *load,
- struct ofpbuf *openflow)
-{
-
- if (load->ofpact.compat == OFPUTIL_OFPAT12_SET_FIELD) {
- struct ofp_header *oh = (struct ofp_header *)openflow->l2;
-
- switch(oh->version) {
- case OFP13_VERSION:
- case OFP12_VERSION:
- set_field_to_ofast(load, openflow);
- break;
-
- case OFP11_VERSION:
- case OFP10_VERSION:
- if (load->dst.n_bits < 64) {
- reg_load_to_nxast(load, openflow);
- } else {
- /* Split into 64bit chunks */
- int chunk, ofs;
- for (ofs = 0; ofs < load->dst.n_bits; ofs += chunk) {
- struct ofpact_reg_load subload = *load;
-
- chunk = MIN(load->dst.n_bits - ofs, 64);
-
- subload.dst.field = load->dst.field;
- subload.dst.ofs = load->dst.ofs + ofs;
- subload.dst.n_bits = chunk;
- bitwise_copy(&load->subvalue, sizeof load->subvalue, ofs,
- &subload.subvalue, sizeof subload.subvalue, 0,
- chunk);
- reg_load_to_nxast(&subload, openflow);
- }
- }
- break;
-
- default:
- NOT_REACHED();
- }
- } else {
- reg_load_to_nxast(load, openflow);
- }
-}
\f
/* nxm_execute_reg_move(), nxm_execute_reg_load(). */
struct ofpbuf *ofpacts);
enum ofperr nxm_reg_load_from_openflow(const struct nx_action_reg_load *,
struct ofpbuf *ofpacts);
-enum ofperr nxm_reg_load_from_openflow12_set_field(
- const struct ofp12_action_set_field * oasf, struct ofpbuf *ofpacts);
enum ofperr nxm_reg_move_check(const struct ofpact_reg_move *,
const struct flow *);
}
}
+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);
+ }
+}
+
+static void
+set_field_to_openflow(const struct ofpact_set_field *sf,
+ struct ofpbuf *openflow)
+{
+ struct ofp_header *oh = (struct ofp_header *)openflow->l2;
+
+ if (oh->version >= OFP12_VERSION) {
+ set_field_to_openflow12(sf, openflow);
+ } else {
+ set_field_to_nxast(sf, openflow);
+ }
+}
+
static enum ofperr
output_from_openflow11(const struct ofp11_action_output *oao,
struct ofpbuf *out)
break;
case OFPUTIL_OFPAT12_SET_FIELD:
- return nxm_reg_load_from_openflow12_set_field(&a->set_field, out);
+ return set_field_from_openflow(&a->set_field, out);
case OFPUTIL_OFPAT11_SET_MPLS_TTL:
ofpact_put_SET_MPLS_TTL(out)->ttl = a->ofp11_mpls_ttl.mpls_ttl;
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_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_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:
uint8_t table_id, bool enforce_consistency)
{
const struct ofpact_enqueue *enqueue;
+ const struct mf_field *mf;
switch (a->type) {
case OFPACT_OUTPUT:
case OFPACT_REG_LOAD:
return nxm_reg_load_check(ofpact_get_REG_LOAD(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) ||
+ (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;
+ }
+ return 0;
+
case OFPACT_STACK_PUSH:
return nxm_stack_push_check(ofpact_get_STACK_PUSH(a), flow);
nxm_reg_load_to_nxast(ofpact_get_REG_LOAD(a), out);
break;
+ case OFPACT_SET_FIELD:
+ set_field_to_openflow(ofpact_get_SET_FIELD(a), out);
+ break;
+
case OFPACT_STACK_PUSH:
nxm_stack_push_to_nxast(ofpact_get_STACK_PUSH(a), out);
break;
case OFPACT_BUNDLE:
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_BUNDLE:
case OFPACT_REG_MOVE:
case OFPACT_REG_LOAD:
+ case OFPACT_SET_FIELD:
case OFPACT_STACK_PUSH:
case OFPACT_STACK_POP:
case OFPACT_SET_TUNNEL:
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:
const struct ofpact_metadata *metadata;
const struct ofpact_tunnel *tunnel;
const struct ofpact_sample *sample;
+ const struct ofpact_set_field *set_field;
+ const struct mf_field *mf;
ofp_port_t port;
switch (a->type) {
nxm_format_reg_load(ofpact_get_REG_LOAD(a), s);
break;
+ case OFPACT_SET_FIELD:
+ set_field = ofpact_get_SET_FIELD(a);
+ mf = set_field->field;
+ ds_put_format(s, "set_field:");
+ mf_format(mf, &set_field->value, NULL, s);
+ ds_put_format(s, "->%s", mf->name);
+ break;
+
case OFPACT_STACK_PUSH:
nxm_format_stack_push(ofpact_get_STACK_PUSH(a), s);
break;
ofpbuf_put_zeros(ofpacts, OFPACT_ALIGNTO - rem);
}
}
-
-void
-ofpact_set_field_init(struct ofpact_reg_load *load, const struct mf_field *mf,
- const void *src)
-{
- load->ofpact.compat = OFPUTIL_OFPAT12_SET_FIELD;
- load->dst.field = mf;
- load->dst.ofs = 0;
- load->dst.n_bits = mf->n_bits;
- bitwise_copy(src, mf->n_bytes, load->dst.ofs,
- &load->subvalue, sizeof load->subvalue, 0, mf->n_bits);
-}
DEFINE_OFPACT(BUNDLE, ofpact_bundle, slaves) \
\
/* Header changes. */ \
+ DEFINE_OFPACT(SET_FIELD, ofpact_set_field, ofpact) \
DEFINE_OFPACT(SET_VLAN_VID, ofpact_vlan_vid, ofpact) \
DEFINE_OFPACT(SET_VLAN_PCP, ofpact_vlan_pcp, ofpact) \
DEFINE_OFPACT(STRIP_VLAN, ofpact_null, ofpact) \
/* OFPACT_REG_LOAD.
*
- * Used for NXAST_REG_LOAD, OFPAT12_SET_FIELD. */
+ * Used for NXAST_REG_LOAD. */
struct ofpact_reg_load {
struct ofpact ofpact;
struct mf_subfield dst;
OFPACT_MPLS_AFTER_VLAN
};
+/* OFPACT_SET_FIELD.
+ *
+ * Used for OFPAT12_SET_FIELD. */
+struct ofpact_set_field {
+ struct ofpact ofpact;
+ const struct mf_field *field;
+ union mf_value value;
+};
+
/* OFPACT_PUSH_VLAN/MPLS/PBB
*
* Used for NXAST_PUSH_MPLS, OFPAT11_PUSH_MPLS. */
int ovs_instruction_type_from_name(const char *name);
enum ovs_instruction_type ovs_instruction_type_from_ofpact_type(
enum ofpact_type);
-
-void ofpact_set_field_init(struct ofpact_reg_load *load,
- const struct mf_field *mf, const void *src);
#endif /* ofp-actions.h */
set_field_parse__(char *arg, struct ofpbuf *ofpacts,
enum ofputil_protocol *usable_protocols)
{
- struct ofpact_reg_load *load = ofpact_put_REG_LOAD(ofpacts);
+ struct ofpact_set_field *sf = ofpact_put_SET_FIELD(ofpacts);
char *value;
char *delim;
char *key;
const struct mf_field *mf;
char *error;
- union mf_value mf_value;
value = arg;
delim = strstr(arg, "->");
if (!mf->writable) {
return xasprintf("%s is read-only", key);
}
-
+ sf->field = mf;
delim[0] = '\0';
- error = mf_parse_value(mf, value, &mf_value);
+ error = mf_parse_value(mf, value, &sf->value);
if (error) {
return error;
}
- if (!mf_is_value_valid(mf, &mf_value)) {
+
+ if (!mf_is_value_valid(mf, &sf->value)) {
return xasprintf("%s is not a valid value for field %s", value, key);
}
- ofpact_set_field_init(load, mf, &mf_value);
*usable_protocols &= mf->usable_protocols;
return NULL;
struct ofpbuf;
union ofp_action;
+struct ofpact_set_field;
/* Port numbers. */
enum ofperr ofputil_port_from_ofp11(ovs_be32 ofp11_port,
OFPACT_FOR_EACH (a, ofpacts, ofpacts_len) {
struct ofpact_controller *controller;
const struct ofpact_metadata *metadata;
+ const struct ofpact_set_field *set_field;
+ const struct mf_field *mf;
if (ctx->exit) {
break;
nxm_execute_reg_load(ofpact_get_REG_LOAD(a), flow, wc);
break;
+ case OFPACT_SET_FIELD:
+ set_field = ofpact_get_SET_FIELD(a);
+ mf = set_field->field;
+ mf_mask_field_and_prereqs(mf, &wc->masks);
+
+ /* Set field action only ever overwrites packet's outermost
+ * applicable header fields. Do nothing if no header exists. */
+ if ((mf->id != MFF_VLAN_VID || flow->vlan_tci & htons(VLAN_CFI))
+ && ((mf->id != MFF_MPLS_LABEL && mf->id != MFF_MPLS_TC)
+ || flow->mpls_lse)) {
+ mf_set_flow_value(mf, &set_field->value, flow);
+ }
+ break;
+
case OFPACT_STACK_PUSH:
nxm_execute_stack_push(ofpact_get_STACK_PUSH(a), flow, wc,
&ctx->stack);