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;
case OFPACT_SET_VLAN_VID:
wc->masks.vlan_tci |= htons(VLAN_VID_MASK | VLAN_CFI);
- flow->vlan_tci &= ~htons(VLAN_VID_MASK);
- flow->vlan_tci |= (htons(ofpact_get_SET_VLAN_VID(a)->vlan_vid)
- | htons(VLAN_CFI));
+ if (flow->vlan_tci & htons(VLAN_CFI) ||
+ ofpact_get_SET_VLAN_VID(a)->push_vlan_if_needed) {
+ flow->vlan_tci &= ~htons(VLAN_VID_MASK);
+ flow->vlan_tci |= (htons(ofpact_get_SET_VLAN_VID(a)->vlan_vid)
+ | htons(VLAN_CFI));
+ }
break;
case OFPACT_SET_VLAN_PCP:
wc->masks.vlan_tci |= htons(VLAN_PCP_MASK | VLAN_CFI);
- flow->vlan_tci &= ~htons(VLAN_PCP_MASK);
- flow->vlan_tci |=
- htons((ofpact_get_SET_VLAN_PCP(a)->vlan_pcp << VLAN_PCP_SHIFT)
- | VLAN_CFI);
+ if (flow->vlan_tci & htons(VLAN_CFI) ||
+ ofpact_get_SET_VLAN_PCP(a)->push_vlan_if_needed) {
+ flow->vlan_tci &= ~htons(VLAN_PCP_MASK);
+ flow->vlan_tci |= htons((ofpact_get_SET_VLAN_PCP(a)->vlan_pcp
+ << VLAN_PCP_SHIFT) | VLAN_CFI);
+ }
break;
case OFPACT_STRIP_VLAN:
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);
break;
case OFPACT_GOTO_TABLE: {
- /* It is assumed that goto-table is the last action. */
struct ofpact_goto_table *ogt = ofpact_get_GOTO_TABLE(a);
ovs_assert(ctx->table_id < ogt->table_id);