const struct odp_flow_key *key,
const union odp_action *a, int n_actions)
{
- __be16 mask = a->dl_tci.mask;
__be16 tci = a->dl_tci.tci;
skb = make_writable(skb, VLAN_HLEN);
vh = vlan_eth_hdr(skb);
old_tci = vh->h_vlan_TCI;
- vh->h_vlan_TCI = (vh->h_vlan_TCI & ~mask) | tci;
+ vh->h_vlan_TCI = tci;
if (OVS_CB(skb)->ip_summed == OVS_CSUM_COMPLETE) {
__be16 diff[] = { ~old_tci, vh->h_vlan_TCI };
for (i = 0; i < actions->n_actions; i++) {
const union odp_action *a = &actions->actions[i];
- __be16 mask;
switch (a->type) {
case ODPAT_CONTROLLER:
break;
case ODPAT_SET_DL_TCI:
- mask = a->dl_tci.mask;
- if (mask != htons(VLAN_VID_MASK) &&
- mask != htons(VLAN_PCP_MASK) &&
- mask != htons(VLAN_VID_MASK | VLAN_PCP_MASK))
- return -EINVAL;
- if (a->dl_tci.tci & ~mask)
+ if (a->dl_tci.tci & htons(VLAN_CFI_MASK))
return -EINVAL;
break;
/* Action types. */
#define ODPAT_OUTPUT 0 /* Output to switch port. */
#define ODPAT_CONTROLLER 2 /* Send copy to controller. */
-#define ODPAT_SET_DL_TCI 3 /* Set the 802.1q VLAN VID and/or PCP. */
+#define ODPAT_SET_DL_TCI 3 /* Set the 802.1q TCI value. */
#define ODPAT_STRIP_VLAN 5 /* Strip the 802.1q header. */
#define ODPAT_SET_DL_SRC 6 /* Ethernet source address. */
#define ODPAT_SET_DL_DST 7 /* Ethernet destination address. */
/* Action structure for ODPAT_SET_DL_TCI. */
struct odp_action_dl_tci {
uint16_t type; /* ODPAT_SET_DL_TCI. */
- ovs_be16 tci; /* New TCI. Bits not in mask must be zero. */
- ovs_be16 mask; /* 0x0fff to set VID, 0xe000 to set PCP,
- * or 0xefff to set both. */
- uint16_t reserved;
+ ovs_be16 tci; /* New TCI. CFI bit must be zero. */
+ uint32_t reserved;
};
/* Action structure for ODPAT_SET_DL_SRC/DST. */
case ODPAT_SET_DL_TCI:
*mutates = true;
- if (a->dl_tci.mask != htons(VLAN_VID_MASK)
- && a->dl_tci.mask != htons(VLAN_PCP_MASK)
- && a->dl_tci.mask != htons(VLAN_VID_MASK | VLAN_PCP_MASK)) {
- return EINVAL;
- }
- if (a->dl_tci.tci & ~a->dl_tci.mask){
+ if (a->dl_tci.tci & htons(VLAN_CFI)) {
return EINVAL;
}
break;
}
-/* Modify the TCI field of 'packet'. If a VLAN tag is not present, one is
- * added with the TCI field set to 'a->tci'. If a VLAN tag is present, then
- * 'a->mask' bits are cleared before 'a->tci' is logically OR'd into the TCI
- * field.
- *
- * Note that the function does not ensure that 'a->tci' does not affect bits
- * outside of 'a->mask'.
+/* Modify the TCI field of 'packet'. If a VLAN tag is present, its TCI field
+ * is replaced by 'tci'. If a VLAN tag is not present, one is added with the
+ * TCI field set to 'tci'.
*/
static void
-dp_netdev_set_dl_tci(struct ofpbuf *packet, const struct odp_action_dl_tci *a)
+dp_netdev_set_dl_tci(struct ofpbuf *packet, uint16_t tci)
{
struct vlan_eth_header *veh;
struct eth_header *eh;
eh = packet->l2;
if (packet->size >= sizeof(struct vlan_eth_header)
&& eh->eth_type == htons(ETH_TYPE_VLAN)) {
- /* Clear 'mask' bits, but maintain other TCI bits. */
veh = packet->l2;
- veh->veth_tci = (veh->veth_tci & ~a->mask) | a->tci;
+ veh->veth_tci = tci;
} else {
/* Insert new 802.1Q header. */
struct vlan_eth_header tmp;
memcpy(tmp.veth_dst, eh->eth_dst, ETH_ADDR_LEN);
memcpy(tmp.veth_src, eh->eth_src, ETH_ADDR_LEN);
tmp.veth_type = htons(ETH_TYPE_VLAN);
- tmp.veth_tci = htons(a->tci);
+ tmp.veth_tci = tci;
tmp.veth_next_type = eh->eth_type;
veh = ofpbuf_push_uninit(packet, VLAN_HEADER_LEN);
break;
case ODPAT_SET_DL_TCI:
- dp_netdev_set_dl_tci(packet, &a->dl_tci);
+ dp_netdev_set_dl_tci(packet, a->dl_tci.tci);
break;
case ODPAT_STRIP_VLAN:
case ODPAT_SET_TUNNEL:
ds_put_format(ds, "set_tunnel(0x%08"PRIx32")", ntohl(a->tunnel.tun_id));
break;
- case ODPAT_SET_DL_TCI: {
- int vid = vlan_tci_to_vid(a->dl_tci.tci);
- int pcp = vlan_tci_to_pcp(a->dl_tci.tci);
-
- switch (ntohs(a->dl_tci.mask)) {
- case VLAN_VID_MASK:
- ds_put_format(ds, "set_tci(vlan=%d)", vid);
- break;
- case VLAN_PCP_MASK:
- ds_put_format(ds, "set_tci(pcp=%d)", pcp);
- break;
- case VLAN_VID_MASK | VLAN_PCP_MASK:
- ds_put_format(ds, "set_tci(vlan=%d,pcp=%d)", vid, pcp);
- break;
- default:
- ds_put_format(ds, "set_tci(tci=%04"PRIx16",mask=%04"PRIx16")",
- ntohs(a->dl_tci.tci), ntohs(a->dl_tci.mask));
- break;
- }
- }
+ case ODPAT_SET_DL_TCI:
+ ds_put_format(ds, "set_tci(vid=%"PRIu16",pcp=%d)",
+ vlan_tci_to_vid(a->dl_tci.tci),
+ vlan_tci_to_pcp(a->dl_tci.tci));
break;
case ODPAT_STRIP_VLAN:
ds_put_format(ds, "strip_vlan");
return check_output_port(ntohs(a->output.port), max_ports);
case OFPAT_SET_VLAN_VID:
+ error = check_action_exact_len(a, len, 8);
+ if (error) {
+ return error;
+ }
+ if (a->vlan_vid.vlan_vid & ~htons(0xfff)) {
+ return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT);
+ }
+ return 0;
+
case OFPAT_SET_VLAN_PCP:
+ error = check_action_exact_len(a, len, 8);
+ if (error) {
+ return error;
+ }
+ if (a->vlan_vid.vlan_vid & ~7) {
+ return ofp_mkerr(OFPET_BAD_ACTION, OFPBAC_BAD_ARGUMENT);
+ }
+ return 0;
+
case OFPAT_STRIP_VLAN:
case OFPAT_SET_NW_SRC:
case OFPAT_SET_NW_DST:
n_outputs = 0;
for (i = 0; i < n_actions; i++) {
const union odp_action *a = &actions[i];
+ uint16_t tci;
switch (a->type) {
case ODPAT_OUTPUT:
break;
case ODPAT_SET_DL_TCI:
- if (a->dl_tci.mask & htons(VLAN_VID_MASK)) {
- switchElem.flowType.sw.dst_vlan = vlan_tci_to_vid(a->dl_tci.tci);
- }
- if (a->dl_tci.mask & htons(VLAN_PCP_MASK)) {
- switchElem.flowType.sw.dst_priority = vlan_tci_to_pcp(a->dl_tci.tci);
- }
+ tci = a->dl_tci.tci;
+ switchElem.flowType.sw.dst_vlan = vlan_tci_to_vid(tci);
+ switchElem.flowType.sw.dst_priority = vlan_tci_to_pcp(tci);
break;
default:
case OFPAT_SET_VLAN_VID:
oa = odp_actions_add(ctx->out, ODPAT_SET_DL_TCI);
- oa->dl_tci.tci = ia->vlan_vid.vlan_vid & htons(VLAN_VID_MASK);
- oa->dl_tci.mask = htons(VLAN_VID_MASK);
+ oa->dl_tci.tci = ia->vlan_vid.vlan_vid;
+ oa->dl_tci.tci |= htons(ctx->flow.dl_vlan_pcp << VLAN_PCP_SHIFT);
ctx->flow.dl_vlan = ia->vlan_vid.vlan_vid;
break;
case OFPAT_SET_VLAN_PCP:
oa = odp_actions_add(ctx->out, ODPAT_SET_DL_TCI);
- oa->dl_tci.tci = htons((ia->vlan_pcp.vlan_pcp << VLAN_PCP_SHIFT)
- & VLAN_PCP_MASK);
- oa->dl_tci.mask = htons(VLAN_PCP_MASK);
+ oa->dl_tci.tci = htons(ia->vlan_pcp.vlan_pcp << VLAN_PCP_SHIFT);
+ oa->dl_tci.tci |= ctx->flow.dl_vlan;
ctx->flow.dl_vlan_pcp = ia->vlan_pcp.vlan_pcp;
break;
} else {
a = odp_actions_add(actions, ODPAT_SET_DL_TCI);
a->dl_tci.tci = htons(p->vlan & VLAN_VID_MASK);
- a->dl_tci.mask = htons(VLAN_VID_MASK);
+ a->dl_tci.tci |= htons(flow->dl_vlan_pcp << VLAN_PCP_SHIFT);
}
cur_vlan = p->vlan;
}