[ovs-dev] [generic tci mask 8/8] nx-match: Implement support for arbitrary VLAN TCI masks.

Ben Pfaff blp at nicira.com
Wed Nov 10 23:38:03 UTC 2010


Since the Nicira Extended Match was specified nicira-ext.h has claimed that
arbitrary masks are allowed, but in fact only certain masks were actually
implemented.  This commit implements general masking for the 802.1Q VLAN
TCI field.
---
 lib/classifier.c        |  121 +++++++++++++++++++++--------------------------
 lib/classifier.h        |    6 ++-
 lib/flow.c              |   37 +++++++++------
 lib/flow.h              |   19 ++++----
 lib/nx-match.c          |   88 ++++++++++++++--------------------
 lib/odp-util.c          |   17 +------
 lib/ofp-parse.c         |    8 ++-
 lib/ofp-util.c          |   75 ++++++++++++++++++++++++-----
 ofproto/ofproto-sflow.c |    4 +-
 ofproto/ofproto.c       |   25 ++++-----
 tests/ovs-ofctl.at      |   11 ++--
 tests/test-classifier.c |   28 +++++------
 vswitchd/bridge.c       |   38 +++++++--------
 13 files changed, 240 insertions(+), 237 deletions(-)

diff --git a/lib/classifier.c b/lib/classifier.c
index 5c09f77..16b9845 100644
--- a/lib/classifier.c
+++ b/lib/classifier.c
@@ -138,84 +138,77 @@ cls_rule_set_dl_dst(struct cls_rule *rule, const uint8_t dl_dst[ETH_ADDR_LEN])
     memcpy(rule->flow.dl_dst, dl_dst, ETH_ADDR_LEN);
 }
 
-bool
+void
 cls_rule_set_dl_tci(struct cls_rule *rule, ovs_be16 tci)
 {
-    return cls_rule_set_dl_tci_masked(rule, tci, htons(0xffff));
+    cls_rule_set_dl_tci_masked(rule, tci, htons(0xffff));
 }
 
-bool
+void
 cls_rule_set_dl_tci_masked(struct cls_rule *rule, ovs_be16 tci, ovs_be16 mask)
 {
-    switch (ntohs(mask)) {
-    case 0xffff:
-        if (tci == htons(0)) {
-            /* Match only packets that have no 802.1Q header. */
-            rule->wc.wildcards &= ~(FWW_DL_VLAN | FWW_DL_VLAN_PCP);
-            rule->flow.dl_vlan = htons(OFP_VLAN_NONE);
-            rule->flow.dl_vlan_pcp = 0;
-            return true;
-        } else if (tci & htons(VLAN_CFI)) {
-            /* Match only packets that have a specific 802.1Q VID and PCP. */
-            rule->wc.wildcards &= ~(FWW_DL_VLAN | FWW_DL_VLAN_PCP);
-            rule->flow.dl_vlan = htons(vlan_tci_to_vid(tci));
-            rule->flow.dl_vlan_pcp = vlan_tci_to_pcp(tci);
-            return true;
-        } else {
-            /* Impossible. */
-            return false;
-        }
-
-    case 0x1fff:
-        if (!(tci & htons(VLAN_CFI))) {
-            return false;
-        } else {
-            /* Match only packets that have a specific 802.1Q VID. */
-            cls_rule_set_dl_vlan(rule, tci & htons(VLAN_VID_MASK));
-            rule->wc.wildcards |= FWW_DL_VLAN_PCP;
-            rule->flow.dl_vlan_pcp = 0;
-            return true;
-        }
-
-    case 0xf000:
-        if (!(tci & htons(VLAN_CFI))) {
-            return false;
-        } else {
-            /* Match only packets that have a specific 802.1Q PCP. */
-            cls_rule_set_dl_vlan_pcp(rule, vlan_tci_to_pcp(tci));
-            rule->wc.wildcards |= FWW_DL_VLAN;
-            rule->flow.dl_vlan = 0;
-            return true;
-        }
-
-    case 0x0000:
-        /* Match anything. */
-        rule->wc.wildcards |= FWW_DL_VLAN | FWW_DL_VLAN_PCP;
-        rule->flow.dl_vlan = htons(0);
-        rule->flow.dl_vlan_pcp = 0;
-        return true;
+    rule->flow.vlan_tci = tci & mask;
+    rule->wc.vlan_tci_mask = mask;
+}
 
-    default:
-        return false;
+/* Modifies 'rule' so that the VLAN VID is wildcarded.  If the PCP is already
+ * wildcarded, then 'rule' will match a packet regardless of whether it has an
+ * 802.1Q header or not. */
+void
+cls_rule_set_any_vid(struct cls_rule *rule)
+{
+    if (rule->wc.vlan_tci_mask & htons(VLAN_PCP_MASK)) {
+        rule->wc.vlan_tci_mask &= htons(VLAN_VID_MASK);
+        rule->flow.vlan_tci &= htons(VLAN_VID_MASK);
+    } else {
+        cls_rule_set_dl_tci_masked(rule, htons(0), htons(0));
     }
 }
 
+/* Modifies 'rule' depending on 'dl_vlan':
+ *
+ *   - If 'dl_vlan' is htons(OFP_VLAN_NONE), makes 'rule' match only packets
+ *     without an 802.1Q header.
+ *
+ *   - Otherwise, makes 'rule' match only packets with an 802.1Q header whose
+ *     VID equals the low 12 bits of 'dl_vlan'.
+ */
 void
 cls_rule_set_dl_vlan(struct cls_rule *rule, ovs_be16 dl_vlan)
 {
-    if (dl_vlan != htons(OFP_VLAN_NONE)) {
+    if (dl_vlan == htons(OFP_VLAN_NONE)) {
+        cls_rule_set_dl_tci(rule, htons(0));
+    } else {
         dl_vlan &= htons(VLAN_VID_MASK);
+        rule->flow.vlan_tci &= ~htons(VLAN_VID_MASK);
+        rule->flow.vlan_tci |= htons(VLAN_CFI) | dl_vlan;
+        rule->wc.vlan_tci_mask |= htons(VLAN_VID_MASK | VLAN_CFI);
     }
+}
 
-    rule->wc.wildcards &= ~FWW_DL_VLAN;
-    rule->flow.dl_vlan = dl_vlan;
+/* Modifies 'rule' so that the VLAN PCP is wildcarded.  If the VID is already
+ * wildcarded, then 'rule' will match a packet regardless of whether it has an
+ * 802.1Q header or not. */
+void
+cls_rule_set_any_pcp(struct cls_rule *rule)
+{
+    if (rule->wc.vlan_tci_mask & htons(VLAN_VID_MASK)) {
+        rule->wc.vlan_tci_mask &= htons(VLAN_PCP_MASK);
+        rule->flow.vlan_tci &= htons(VLAN_PCP_MASK);
+    } else {
+        cls_rule_set_dl_tci_masked(rule, htons(0), htons(0));
+    }
 }
 
+/* Modifies 'rule' so that it matches only packets with an 802.1Q header whose
+ * PCP equals the low 3 bits of 'dl_vlan_pcp'. */
 void
 cls_rule_set_dl_vlan_pcp(struct cls_rule *rule, uint8_t dl_vlan_pcp)
 {
-    rule->wc.wildcards &= ~FWW_DL_VLAN_PCP;
-    rule->flow.dl_vlan_pcp = dl_vlan_pcp & 0x07;
+    dl_vlan_pcp &= 0x07;
+    rule->flow.vlan_tci &= ~htons(VLAN_PCP_MASK);
+    rule->flow.vlan_tci |= htons((dl_vlan_pcp << VLAN_PCP_SHIFT) | VLAN_CFI);
+    rule->wc.vlan_tci_mask |= htons(VLAN_CFI | VLAN_PCP_MASK);
 }
 
 void
@@ -756,7 +749,7 @@ flow_equal_except(const struct flow *a, const struct flow *b,
     const flow_wildcards_t wc = wildcards->wildcards;
     int i;
 
-    BUILD_ASSERT_DECL(FLOW_SIG_SIZE == 37 + FLOW_N_REGS * 4);
+    BUILD_ASSERT_DECL(FLOW_SIG_SIZE == 36 + FLOW_N_REGS * 4);
 
     for (i = 0; i < FLOW_N_REGS; i++) {
         if ((a->regs[i] ^ b->regs[i]) & wildcards->reg_masks[i]) {
@@ -768,7 +761,7 @@ flow_equal_except(const struct flow *a, const struct flow *b,
             && !((a->nw_src ^ b->nw_src) & wildcards->nw_src_mask)
             && !((a->nw_dst ^ b->nw_dst) & wildcards->nw_dst_mask)
             && (wc & FWW_IN_PORT || a->in_port == b->in_port)
-            && (wc & FWW_DL_VLAN || a->dl_vlan == b->dl_vlan)
+            && !((a->vlan_tci ^ b->vlan_tci) & wildcards->vlan_tci_mask)
             && (wc & FWW_DL_TYPE || a->dl_type == b->dl_type)
             && (wc & FWW_TP_SRC || a->tp_src == b->tp_src)
             && (wc & FWW_TP_DST || a->tp_dst == b->tp_dst)
@@ -783,7 +776,6 @@ flow_equal_except(const struct flow *a, const struct flow *b,
             && (wc & FWW_ETH_MCAST
                 || !((a->dl_dst[0] ^ b->dl_dst[0]) & 0x01))
             && (wc & FWW_NW_PROTO || a->nw_proto == b->nw_proto)
-            && (wc & FWW_DL_VLAN_PCP || a->dl_vlan_pcp == b->dl_vlan_pcp)
             && (wc & FWW_NW_TOS || a->nw_tos == b->nw_tos));
 }
 
@@ -793,7 +785,7 @@ zero_wildcards(struct flow *flow, const struct flow_wildcards *wildcards)
     const flow_wildcards_t wc = wildcards->wildcards;
     int i;
 
-    BUILD_ASSERT_DECL(FLOW_SIG_SIZE == 37 + 4 * FLOW_N_REGS);
+    BUILD_ASSERT_DECL(FLOW_SIG_SIZE == 36 + 4 * FLOW_N_REGS);
 
     for (i = 0; i < FLOW_N_REGS; i++) {
         flow->regs[i] &= wildcards->reg_masks[i];
@@ -806,9 +798,7 @@ zero_wildcards(struct flow *flow, const struct flow_wildcards *wildcards)
     if (wc & FWW_IN_PORT) {
         flow->in_port = 0;
     }
-    if (wc & FWW_DL_VLAN) {
-        flow->dl_vlan = 0;
-    }
+    flow->vlan_tci &= wildcards->vlan_tci_mask;
     if (wc & FWW_DL_TYPE) {
         flow->dl_type = 0;
     }
@@ -831,9 +821,6 @@ zero_wildcards(struct flow *flow, const struct flow_wildcards *wildcards)
     if (wc & FWW_NW_PROTO) {
         flow->nw_proto = 0;
     }
-    if (wc & FWW_DL_VLAN_PCP) {
-        flow->dl_vlan_pcp = 0;
-    }
     if (wc & FWW_NW_TOS) {
         flow->nw_tos = 0;
     }
diff --git a/lib/classifier.h b/lib/classifier.h
index e4b7f5f..2dc0bdf 100644
--- a/lib/classifier.h
+++ b/lib/classifier.h
@@ -78,10 +78,12 @@ void cls_rule_set_in_port(struct cls_rule *, uint16_t odp_port);
 void cls_rule_set_dl_type(struct cls_rule *, ovs_be16);
 void cls_rule_set_dl_src(struct cls_rule *, const uint8_t[6]);
 void cls_rule_set_dl_dst(struct cls_rule *, const uint8_t[6]);
-bool cls_rule_set_dl_tci(struct cls_rule *, ovs_be16 tci);
-bool cls_rule_set_dl_tci_masked(struct cls_rule *,
+void cls_rule_set_dl_tci(struct cls_rule *, ovs_be16 tci);
+void cls_rule_set_dl_tci_masked(struct cls_rule *,
                                 ovs_be16 tci, ovs_be16 mask);
+void cls_rule_set_any_vid(struct cls_rule *);
 void cls_rule_set_dl_vlan(struct cls_rule *, ovs_be16);
+void cls_rule_set_any_pcp(struct cls_rule *);
 void cls_rule_set_dl_vlan_pcp(struct cls_rule *, uint8_t);
 void cls_rule_set_tp_src(struct cls_rule *, ovs_be16);
 void cls_rule_set_tp_dst(struct cls_rule *, ovs_be16);
diff --git a/lib/flow.c b/lib/flow.c
index 8146519..26d03fd 100644
--- a/lib/flow.c
+++ b/lib/flow.c
@@ -88,8 +88,7 @@ parse_vlan(struct ofpbuf *b, struct flow *flow)
 
     if (b->size >= sizeof(struct qtag_prefix) + sizeof(ovs_be16)) {
         struct qtag_prefix *qp = ofpbuf_pull(b, sizeof *qp);
-        flow->dl_vlan = qp->tci & htons(VLAN_VID_MASK);
-        flow->dl_vlan_pcp = vlan_tci_to_pcp(qp->tci);
+        flow->vlan_tci = qp->tci | htons(VLAN_CFI);
     }
 }
 
@@ -149,7 +148,6 @@ flow_extract(struct ofpbuf *packet, ovs_be32 tun_id, uint16_t in_port,
     memset(flow, 0, sizeof *flow);
     flow->tun_id = tun_id;
     flow->in_port = in_port;
-    flow->dl_vlan = htons(OFP_VLAN_NONE);
 
     packet->l2 = b.data;
     packet->l3 = NULL;
@@ -165,7 +163,7 @@ flow_extract(struct ofpbuf *packet, ovs_be32 tun_id, uint16_t in_port,
     memcpy(flow->dl_src, eth->eth_src, ETH_ADDR_LEN);
     memcpy(flow->dl_dst, eth->eth_dst, ETH_ADDR_LEN);
 
-    /* dl_type, dl_vlan, dl_vlan_pcp. */
+    /* dl_type, vlan_tci. */
     ofpbuf_pull(&b, ETH_ADDR_LEN * 2);
     if (eth->eth_type == htons(ETH_TYPE_VLAN)) {
         parse_vlan(&b, flow);
@@ -261,18 +259,21 @@ flow_to_string(const struct flow *flow)
 void
 flow_format(struct ds *ds, const struct flow *flow)
 {
-    ds_put_format(ds, "tunnel%08"PRIx32":in_port%04"PRIx16
-                      ":vlan%"PRIu16":pcp%"PRIu8
-                      " mac"ETH_ADDR_FMT"->"ETH_ADDR_FMT
+    ds_put_format(ds, "tunnel%08"PRIx32":in_port%04"PRIx16":tci(",
+                  ntohl(flow->tun_id), flow->in_port);
+    if (flow->vlan_tci) {
+        ds_put_format(ds, "vlan%"PRIu16",pcp%d",
+                      vlan_tci_to_vid(flow->vlan_tci),
+                      vlan_tci_to_pcp(flow->vlan_tci));
+    } else {
+        ds_put_char(ds, '0');
+    }
+    ds_put_format(ds, ") mac"ETH_ADDR_FMT"->"ETH_ADDR_FMT
                       " type%04"PRIx16
                       " proto%"PRIu8
                       " tos%"PRIu8
                       " ip"IP_FMT"->"IP_FMT
                       " port%"PRIu16"->%"PRIu16,
-                  ntohl(flow->tun_id),
-                  flow->in_port,
-                  ntohs(flow->dl_vlan),
-                  flow->dl_vlan_pcp,
                   ETH_ADDR_ARGS(flow->dl_src),
                   ETH_ADDR_ARGS(flow->dl_dst),
                   ntohs(flow->dl_type),
@@ -302,6 +303,7 @@ flow_wildcards_init_catchall(struct flow_wildcards *wc)
     wc->nw_src_mask = htonl(0);
     wc->nw_dst_mask = htonl(0);
     memset(wc->reg_masks, 0, sizeof wc->reg_masks);
+    wc->vlan_tci_mask = htons(0);
 }
 
 /* Initializes 'wc' as an exact-match set of wildcards; that is, 'wc' does not
@@ -313,6 +315,7 @@ flow_wildcards_init_exact(struct flow_wildcards *wc)
     wc->nw_src_mask = htonl(UINT32_MAX);
     wc->nw_dst_mask = htonl(UINT32_MAX);
     memset(wc->reg_masks, 0xff, sizeof wc->reg_masks);
+    wc->vlan_tci_mask = htons(UINT16_MAX);
 }
 
 /* Returns true if 'wc' is exact-match, false if 'wc' wildcards any bits or
@@ -324,7 +327,8 @@ flow_wildcards_is_exact(const struct flow_wildcards *wc)
 
     if (wc->wildcards
         || wc->nw_src_mask != htonl(UINT32_MAX)
-        || wc->nw_dst_mask != htonl(UINT32_MAX)) {
+        || wc->nw_dst_mask != htonl(UINT32_MAX)
+        || wc->vlan_tci_mask != htons(UINT16_MAX)) {
         return false;
     }
 
@@ -353,6 +357,7 @@ flow_wildcards_combine(struct flow_wildcards *dst,
     for (i = 0; i < FLOW_N_REGS; i++) {
         dst->reg_masks[i] = src1->reg_masks[i] & src2->reg_masks[i];
     }
+    dst->vlan_tci_mask = src1->vlan_tci_mask & src2->vlan_tci_mask;
 }
 
 /* Returns a hash of the wildcards in 'wc'. */
@@ -362,7 +367,7 @@ flow_wildcards_hash(const struct flow_wildcards *wc)
     /* If you change struct flow_wildcards and thereby trigger this
      * assertion, please check that the new struct flow_wildcards has no holes
      * in it before you update the assertion. */
-    BUILD_ASSERT_DECL(sizeof *wc == 12 + FLOW_N_REGS * 4);
+    BUILD_ASSERT_DECL(sizeof *wc == 16 + FLOW_N_REGS * 4);
     return hash_bytes(wc, sizeof *wc, 0);
 }
 
@@ -376,7 +381,8 @@ flow_wildcards_equal(const struct flow_wildcards *a,
 
     if (a->wildcards != b->wildcards
         || a->nw_src_mask != b->nw_src_mask
-        || a->nw_dst_mask != b->nw_dst_mask) {
+        || a->nw_dst_mask != b->nw_dst_mask
+        || a->vlan_tci_mask != b->vlan_tci_mask) {
         return false;
     }
 
@@ -405,7 +411,8 @@ flow_wildcards_has_extra(const struct flow_wildcards *a,
 
     return (a->wildcards & ~b->wildcards
             || (a->nw_src_mask & b->nw_src_mask) != b->nw_src_mask
-            || (a->nw_dst_mask & b->nw_dst_mask) != b->nw_dst_mask);
+            || (a->nw_dst_mask & b->nw_dst_mask) != b->nw_dst_mask
+            || (a->vlan_tci_mask & b->vlan_tci_mask) != b->vlan_tci_mask);
 }
 
 static bool
diff --git a/lib/flow.h b/lib/flow.h
index f772936..a0dc7a1 100644
--- a/lib/flow.h
+++ b/lib/flow.h
@@ -41,21 +41,20 @@ struct flow {
     ovs_be32 nw_src;            /* IP source address. */
     ovs_be32 nw_dst;            /* IP destination address. */
     uint16_t in_port;           /* Input switch port. */
-    ovs_be16 dl_vlan;           /* Input VLAN. */
+    ovs_be16 vlan_tci;          /* If 802.1Q, TCI | VLAN_CFI; otherwise 0. */
     ovs_be16 dl_type;           /* Ethernet frame type. */
     ovs_be16 tp_src;            /* TCP/UDP source port. */
     ovs_be16 tp_dst;            /* TCP/UDP destination port. */
     uint8_t dl_src[6];          /* Ethernet source address. */
     uint8_t dl_dst[6];          /* Ethernet destination address. */
     uint8_t nw_proto;           /* IP protocol or low 8 bits of ARP opcode. */
-    uint8_t dl_vlan_pcp;        /* Input VLAN priority. */
     uint8_t nw_tos;             /* IP ToS (DSCP field, 6 bits). */
 };
 
 /* Assert that there are FLOW_SIG_SIZE bytes of significant data in "struct
  * flow", followed by FLOW_PAD_SIZE bytes of padding. */
-#define FLOW_SIG_SIZE (37 + FLOW_N_REGS * 4)
-#define FLOW_PAD_SIZE 3
+#define FLOW_SIG_SIZE (36 + FLOW_N_REGS * 4)
+#define FLOW_PAD_SIZE 0
 BUILD_ASSERT_DECL(offsetof(struct flow, nw_tos) == FLOW_SIG_SIZE - 1);
 BUILD_ASSERT_DECL(sizeof(((struct flow *)0)->nw_tos) == 1);
 BUILD_ASSERT_DECL(sizeof(struct flow) == FLOW_SIG_SIZE + FLOW_PAD_SIZE);
@@ -99,7 +98,6 @@ typedef unsigned int OVS_BITWISE flow_wildcards_t;
 
 /* Same values and meanings as corresponding OFPFW_* bits. */
 #define FWW_IN_PORT     ((OVS_FORCE flow_wildcards_t) (1 << 0))
-#define FWW_DL_VLAN     ((OVS_FORCE flow_wildcards_t) (1 << 1))
 #define FWW_DL_SRC      ((OVS_FORCE flow_wildcards_t) (1 << 2))
 #define FWW_DL_DST      ((OVS_FORCE flow_wildcards_t) (1 << 3))
                                               /* excluding the multicast bit */
@@ -108,14 +106,14 @@ typedef unsigned int OVS_BITWISE flow_wildcards_t;
 #define FWW_TP_SRC      ((OVS_FORCE flow_wildcards_t) (1 << 6))
 #define FWW_TP_DST      ((OVS_FORCE flow_wildcards_t) (1 << 7))
 /* Same meanings as corresponding OFPFW_* bits, but differ in value. */
-#define FWW_DL_VLAN_PCP ((OVS_FORCE flow_wildcards_t) (1 << 8))
-#define FWW_NW_TOS      ((OVS_FORCE flow_wildcards_t) (1 << 9))
+#define FWW_NW_TOS      ((OVS_FORCE flow_wildcards_t) (1 << 1))
 /* No OFPFW_* bits, but they do have corresponding OVSFW_* bits. */
-#define FWW_TUN_ID      ((OVS_FORCE flow_wildcards_t) (1 << 10))
+#define FWW_TUN_ID      ((OVS_FORCE flow_wildcards_t) (1 << 8))
 /* No corresponding OFPFW_* or OVSFW_* bits. */
-#define FWW_ETH_MCAST   ((OVS_FORCE flow_wildcards_t) (1 << 11))
+#define FWW_VLAN_TCI    ((OVS_FORCE flow_wildcards_t) (1 << 9)
+#define FWW_ETH_MCAST   ((OVS_FORCE flow_wildcards_t) (1 << 10))
                                                        /* multicast bit only */
-#define FWW_ALL         ((OVS_FORCE flow_wildcards_t) (((1 << 12)) - 1))
+#define FWW_ALL         ((OVS_FORCE flow_wildcards_t) (((1 << 11)) - 1))
 
 /* Information on wildcards for a flow, as a supplement to "struct flow".
  *
@@ -126,6 +124,7 @@ struct flow_wildcards {
     uint32_t reg_masks[FLOW_N_REGS]; /* 1-bit in each significant regs bit. */
     ovs_be32 nw_src_mask;       /* 1-bit in each significant nw_src bit. */
     ovs_be32 nw_dst_mask;       /* 1-bit in each significant nw_dst bit. */
+    ovs_be16 vlan_tci_mask;     /* 1-bit in each significant vlan_tci bit. */
 };
 
 void flow_wildcards_init_catchall(struct flow_wildcards *);
diff --git a/lib/nx-match.c b/lib/nx-match.c
index 210ca9a..e62e5b7 100644
--- a/lib/nx-match.c
+++ b/lib/nx-match.c
@@ -141,18 +141,6 @@ nxm_field_bits(uint32_t header)
 /* nx_pull_match() and helpers. */
 
 static int
-parse_tci(struct cls_rule *rule, ovs_be16 tci, ovs_be16 mask)
-{
-    const flow_wildcards_t FWW_DL_TCI = FWW_DL_VLAN | FWW_DL_VLAN_PCP;
-
-    if ((rule->wc.wildcards & FWW_DL_TCI) != FWW_DL_TCI) {
-        return NXM_DUP_TYPE;
-    } else {
-        return cls_rule_set_dl_tci_masked(rule, tci, mask) ? 0 : NXM_INVALID;
-    }
-}
-
-static int
 parse_nx_reg(const struct nxm_field *f,
              struct flow *flow, struct flow_wildcards *wc,
              const void *value, const void *maskp)
@@ -226,11 +214,20 @@ parse_nxm_entry(struct cls_rule *rule, const struct nxm_field *f,
 
         /* 802.1Q header. */
     case NFI_NXM_OF_VLAN_TCI:
-        return parse_tci(rule, get_unaligned_u16(value), htons(UINT16_MAX));
-
+        if (wc->vlan_tci_mask) {
+            return NXM_DUP_TYPE;
+        } else {
+            cls_rule_set_dl_tci(rule, get_unaligned_u16(value));
+            return 0;
+        }
     case NFI_NXM_OF_VLAN_TCI_W:
-        return parse_tci(rule, get_unaligned_u16(value),
-                         get_unaligned_u16(mask));
+        if (wc->vlan_tci_mask) {
+            return NXM_DUP_TYPE;
+        } else {
+            cls_rule_set_dl_tci_masked(rule, get_unaligned_u16(value),
+                                       get_unaligned_u16(mask));
+            return 0;
+        }
 
         /* IP header. */
     case NFI_NXM_OF_IP_TOS:
@@ -479,6 +476,23 @@ nxm_put_16w(struct ofpbuf *b, uint32_t header, ovs_be16 value, ovs_be16 mask)
 }
 
 static void
+nxm_put_16m(struct ofpbuf *b, uint32_t header, ovs_be16 value, ovs_be16 mask)
+{
+    switch (mask) {
+    case 0:
+        break;
+
+    case CONSTANT_HTONS(UINT16_MAX):
+        nxm_put_16(b, header, value);
+        break;
+
+    default:
+        nxm_put_16w(b, NXM_MAKE_WILD_HEADER(header), value, mask);
+        break;
+    }
+}
+
+static void
 nxm_put_32(struct ofpbuf *b, uint32_t header, ovs_be32 value)
 {
     nxm_put_header(b, header);
@@ -500,7 +514,7 @@ nxm_put_32m(struct ofpbuf *b, uint32_t header, ovs_be32 value, ovs_be32 mask)
     case 0:
         break;
 
-    case UINT32_MAX:
+    case CONSTANT_HTONL(UINT32_MAX):
         nxm_put_32(b, header, value);
         break;
 
@@ -555,7 +569,6 @@ nx_put_match(struct ofpbuf *b, const struct cls_rule *cr)
     const flow_wildcards_t wc = cr->wc.wildcards;
     const struct flow *flow = &cr->flow;
     const size_t start_len = b->size;
-    ovs_be16 vid, pcp;
     int match_len;
     int i;
 
@@ -578,32 +591,9 @@ nx_put_match(struct ofpbuf *b, const struct cls_rule *cr)
     }
 
     /* 802.1Q. */
-    vid = flow->dl_vlan & htons(VLAN_VID_MASK);
-    pcp = htons((flow->dl_vlan_pcp << VLAN_PCP_SHIFT) & VLAN_PCP_MASK);
-    switch (wc & (FWW_DL_VLAN | FWW_DL_VLAN_PCP)) {
-    case FWW_DL_VLAN | FWW_DL_VLAN_PCP:
-        break;
-    case FWW_DL_VLAN:
-        nxm_put_16w(b, NXM_OF_VLAN_TCI_W, pcp | htons(VLAN_CFI),
-                     htons(VLAN_PCP_MASK | VLAN_CFI));
-        break;
-    case FWW_DL_VLAN_PCP:
-        if (flow->dl_vlan == htons(OFP_VLAN_NONE)) {
-            nxm_put_16(b, NXM_OF_VLAN_TCI, 0);
-        } else {
-            nxm_put_16w(b, NXM_OF_VLAN_TCI_W, vid | htons(VLAN_CFI),
-                         htons(VLAN_VID_MASK | VLAN_CFI));
-        }
-        break;
-    case 0:
-        if (flow->dl_vlan == htons(OFP_VLAN_NONE)) {
-            nxm_put_16(b, NXM_OF_VLAN_TCI, 0);
-        } else {
-            nxm_put_16(b, NXM_OF_VLAN_TCI, vid | pcp | htons(VLAN_CFI));
-        }
-        break;
-    }
+    nxm_put_16m(b, NXM_OF_VLAN_TCI, flow->vlan_tci, cr->wc.vlan_tci_mask);
 
+    /* L3. */
     if (!(wc & FWW_DL_TYPE) && flow->dl_type == htons(ETH_TYPE_IP)) {
         /* IP. */
         if (!(wc & FWW_NW_TOS)) {
@@ -905,13 +895,7 @@ nxm_read_field(const struct nxm_field *src, const struct flow *flow)
         return ntohs(flow->dl_type);
 
     case NFI_NXM_OF_VLAN_TCI:
-        if (flow->dl_vlan == htons(OFP_VLAN_NONE)) {
-            return 0;
-        } else {
-            return (ntohs(flow->dl_vlan & htons(VLAN_VID_MASK))
-                    | ((flow->dl_vlan_pcp << VLAN_PCP_SHIFT) & VLAN_PCP_MASK)
-                    | VLAN_CFI);
-        }
+        return ntohs(flow->vlan_tci);
 
     case NFI_NXM_OF_IP_TOS:
         return flow->nw_tos;
@@ -1003,9 +987,7 @@ nxm_execute_reg_move(const struct nx_action_reg_move *action,
     if (NXM_IS_NX_REG(dst->header)) {
         flow->regs[NXM_NX_REG_IDX(dst->header)] = new_data;
     } else if (dst->header == NXM_OF_VLAN_TCI) {
-        ovs_be16 vlan_tci = htons(new_data & VLAN_CFI ? new_data : 0);
-        flow->dl_vlan = htons(vlan_tci_to_vid(vlan_tci));
-        flow->dl_vlan_pcp = vlan_tci_to_pcp(vlan_tci);
+        flow->vlan_tci = htons(new_data);
     } else if (dst->header == NXM_NX_TUN_ID) {
         flow->tun_id = htonl(new_data);
     } else {
diff --git a/lib/odp-util.c b/lib/odp-util.c
index e57a7cb..0c8caab 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -167,14 +167,7 @@ odp_flow_key_from_flow(struct odp_flow_key *key, const struct flow *flow)
     key->nw_src = flow->nw_src;
     key->nw_dst = flow->nw_dst;
     key->in_port = flow->in_port;
-    if (flow->dl_vlan == htons(OFP_VLAN_NONE)) {
-        key->dl_tci = htons(0);
-    } else {
-        uint16_t vid = flow->dl_vlan & htons(VLAN_VID_MASK);
-        uint16_t pcp = htons((flow->dl_vlan_pcp << VLAN_PCP_SHIFT)
-                             & VLAN_PCP_MASK);
-        key->dl_tci = vid | pcp | htons(ODP_TCI_PRESENT);
-    }
+    key->dl_tci = flow->vlan_tci;
     key->dl_type = flow->dl_type;
     key->tp_src = flow->tp_src;
     key->tp_dst = flow->tp_dst;
@@ -192,13 +185,7 @@ odp_flow_key_to_flow(const struct odp_flow_key *key, struct flow *flow)
     flow->nw_src = key->nw_src;
     flow->nw_dst = key->nw_dst;
     flow->in_port = key->in_port;
-    if (key->dl_tci) {
-        flow->dl_vlan = htons(vlan_tci_to_vid(key->dl_tci));
-        flow->dl_vlan_pcp = vlan_tci_to_pcp(key->dl_tci);
-    } else {
-        flow->dl_vlan = htons(OFP_VLAN_NONE);
-        flow->dl_vlan_pcp = 0;
-    }
+    flow->vlan_tci = key->dl_tci;
     flow->dl_type = key->dl_type;
     flow->tp_src = key->tp_src;
     flow->tp_dst = key->tp_dst;
diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c
index 4de9708..f15cee1 100644
--- a/lib/ofp-parse.c
+++ b/lib/ofp-parse.c
@@ -346,8 +346,8 @@ parse_protocol(const char *name, const struct protocol **p_out)
 
 #define FIELDS                                              \
     FIELD(F_IN_PORT,     "in_port",     FWW_IN_PORT)        \
-    FIELD(F_DL_VLAN,     "dl_vlan",     FWW_DL_VLAN)        \
-    FIELD(F_DL_VLAN_PCP, "dl_vlan_pcp", FWW_DL_VLAN_PCP)    \
+    FIELD(F_DL_VLAN,     "dl_vlan",     0)                  \
+    FIELD(F_DL_VLAN_PCP, "dl_vlan_pcp", 0)                  \
     FIELD(F_DL_SRC,      "dl_src",      FWW_DL_SRC)         \
     FIELD(F_DL_DST,      "dl_dst",      FWW_DL_DST)         \
     FIELD(F_DL_TYPE,     "dl_type",     FWW_DL_TYPE)        \
@@ -543,6 +543,10 @@ parse_ofp_str(struct parsed_flow *pf, struct ofpbuf *actions, char *string)
                         cls_rule_set_nw_src_masked(&pf->rule, 0, 0);
                     } else if (f->index == F_NW_DST) {
                         cls_rule_set_nw_dst_masked(&pf->rule, 0, 0);
+                    } else if (f->index == F_DL_VLAN) {
+                        cls_rule_set_any_vid(&pf->rule);
+                    } else if (f->index == F_DL_VLAN_PCP) {
+                        cls_rule_set_any_pcp(&pf->rule);
                     } else {
                         NOT_REACHED();
                     }
diff --git a/lib/ofp-util.c b/lib/ofp-util.c
index b82dd67..20d961e 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -72,7 +72,6 @@ ofputil_netmask_to_wcbits(ovs_be32 netmask)
  * name. */
 #define WC_INVARIANT_LIST \
     WC_INVARIANT_BIT(IN_PORT) \
-    WC_INVARIANT_BIT(DL_VLAN) \
     WC_INVARIANT_BIT(DL_SRC) \
     WC_INVARIANT_BIT(DL_DST) \
     WC_INVARIANT_BIT(DL_TYPE) \
@@ -109,6 +108,7 @@ ofputil_cls_rule_from_match(const struct ofp_match *match,
 {
     struct flow_wildcards *wc = &rule->wc;
     unsigned int ofpfw;
+    ovs_be16 vid, pcp;
 
     /* Initialize rule->priority. */
     ofpfw = ntohl(match->wildcards);
@@ -117,9 +117,6 @@ ofputil_cls_rule_from_match(const struct ofp_match *match,
 
     /* Initialize most of rule->wc. */
     wc->wildcards = ofpfw & WC_INVARIANTS;
-    if (ofpfw & OFPFW_DL_VLAN_PCP) {
-        wc->wildcards |= FWW_DL_VLAN_PCP;
-    }
     if (ofpfw & OFPFW_NW_TOS) {
         wc->wildcards |= FWW_NW_TOS;
     }
@@ -138,13 +135,11 @@ ofputil_cls_rule_from_match(const struct ofp_match *match,
         wc->wildcards |= FWW_ETH_MCAST;
     }
 
-    /* Initialize rule->flow. */
+    /* Initialize most of rule->flow. */
     rule->flow.nw_src = match->nw_src;
     rule->flow.nw_dst = match->nw_dst;
     rule->flow.in_port = (match->in_port == htons(OFPP_LOCAL) ? ODPP_LOCAL
                      : ntohs(match->in_port));
-    rule->flow.dl_vlan = match->dl_vlan;
-    rule->flow.dl_vlan_pcp = match->dl_vlan_pcp;
     rule->flow.dl_type = match->dl_type;
     rule->flow.tp_src = match->tp_src;
     rule->flow.tp_dst = match->tp_dst;
@@ -153,6 +148,41 @@ ofputil_cls_rule_from_match(const struct ofp_match *match,
     rule->flow.nw_tos = match->nw_tos;
     rule->flow.nw_proto = match->nw_proto;
 
+    /* Translate VLANs. */
+    vid = match->dl_vlan & htons(VLAN_VID_MASK);
+    pcp = htons((match->dl_vlan_pcp << VLAN_PCP_SHIFT) & VLAN_PCP_MASK);
+    switch (ofpfw & (OFPFW_DL_VLAN | OFPFW_DL_VLAN_PCP)) {
+    case OFPFW_DL_VLAN | OFPFW_DL_VLAN_PCP:
+        /* Wildcard everything. */
+        rule->flow.vlan_tci = htons(0);
+        rule->wc.vlan_tci_mask = htons(0);
+        break;
+
+    case OFPFW_DL_VLAN_PCP:
+        if (match->dl_vlan == htons(OFP_VLAN_NONE)) {
+            /* Match only packets without 802.1Q header. */
+            rule->flow.vlan_tci = htons(0);
+            rule->wc.vlan_tci_mask = htons(0xffff);
+        } else {
+            /* Wildcard PCP, specific VID. */
+            rule->flow.vlan_tci = vid | htons(VLAN_CFI);
+            rule->wc.vlan_tci_mask = htons(VLAN_VID_MASK | VLAN_CFI);
+        }
+        break;
+
+    case OFPFW_DL_VLAN:
+        /* Wildcard VID, specific PCP. */
+        rule->flow.vlan_tci = pcp | htons(VLAN_CFI);
+        rule->wc.vlan_tci_mask = htons(VLAN_PCP_MASK | VLAN_CFI);
+        break;
+
+    case 0:
+        /* Specific VID and PCP. */
+        rule->flow.vlan_tci = vid | pcp | htons(VLAN_CFI);
+        rule->wc.vlan_tci_mask = htons(0xffff);
+        break;
+    }
+
     /* Clean up. */
     cls_rule_zero_wildcarded_fields(rule);
 }
@@ -170,13 +200,10 @@ ofputil_cls_rule_to_match(const struct cls_rule *rule, int flow_format,
     const struct flow_wildcards *wc = &rule->wc;
     unsigned int ofpfw;
 
-    /* Figure out OpenFlow wildcards. */
+    /* Figure out most OpenFlow wildcards. */
     ofpfw = wc->wildcards & WC_INVARIANTS;
     ofpfw |= ofputil_netmask_to_wcbits(wc->nw_src_mask) << OFPFW_NW_SRC_SHIFT;
     ofpfw |= ofputil_netmask_to_wcbits(wc->nw_dst_mask) << OFPFW_NW_DST_SHIFT;
-    if (wc->wildcards & FWW_DL_VLAN_PCP) {
-        ofpfw |= OFPFW_DL_VLAN_PCP;
-    }
     if (wc->wildcards & FWW_NW_TOS) {
         ofpfw |= OFPFW_NW_TOS;
     }
@@ -184,12 +211,32 @@ ofputil_cls_rule_to_match(const struct cls_rule *rule, int flow_format,
         ofpfw |= NXFW_TUN_ID;
     }
 
-    /* Compose match structure. */
+    /* Translate VLANs. */
+    match->dl_vlan = htons(0);
+    match->dl_vlan_pcp = 0;
+    if (rule->wc.vlan_tci_mask == htons(0)) {
+        ofpfw |= OFPFW_DL_VLAN | OFPFW_DL_VLAN_PCP;
+    } else if (rule->wc.vlan_tci_mask & htons(VLAN_CFI)
+               && !(rule->flow.vlan_tci & htons(VLAN_CFI))) {
+        match->dl_vlan = htons(OFP_VLAN_NONE);
+    } else {
+        if (!(rule->wc.vlan_tci_mask & htons(VLAN_VID_MASK))) {
+            ofpfw |= OFPFW_DL_VLAN;
+        } else {
+            match->dl_vlan = htons(vlan_tci_to_vid(rule->flow.vlan_tci));
+        }
+
+        if (!(rule->wc.vlan_tci_mask & htons(VLAN_PCP_MASK))) {
+            ofpfw |= OFPFW_DL_VLAN_PCP;
+        } else {
+            match->dl_vlan_pcp = vlan_tci_to_pcp(rule->flow.vlan_tci);
+        }
+    }
+
+    /* Compose most match structure. */
     match->wildcards = htonl(ofpfw);
     match->in_port = htons(rule->flow.in_port == ODPP_LOCAL ? OFPP_LOCAL
                            : rule->flow.in_port);
-    match->dl_vlan = rule->flow.dl_vlan;
-    match->dl_vlan_pcp = rule->flow.dl_vlan_pcp;
     memcpy(match->dl_src, rule->flow.dl_src, ETH_ADDR_LEN);
     memcpy(match->dl_dst, rule->flow.dl_dst, ETH_ADDR_LEN);
     match->dl_type = rule->flow.dl_type;
diff --git a/ofproto/ofproto-sflow.c b/ofproto/ofproto-sflow.c
index 04e8347..87abef9 100644
--- a/ofproto/ofproto-sflow.c
+++ b/ofproto/ofproto-sflow.c
@@ -554,8 +554,8 @@ ofproto_sflow_received(struct ofproto_sflow *os, struct odp_msg *msg)
     /* Add extended switch element. */
     memset(&switchElem, 0, sizeof(switchElem));
     switchElem.tag = SFLFLOW_EX_SWITCH;
-    switchElem.flowType.sw.src_vlan = ntohs(flow.dl_vlan);
-    switchElem.flowType.sw.src_priority = -1; /* XXX */
+    switchElem.flowType.sw.src_vlan = vlan_tci_to_vid(flow.vlan_tci);
+    switchElem.flowType.sw.src_priority = vlan_tci_to_pcp(flow.vlan_tci);
      /* Initialize the output VLAN and priority to be the same as the input,
         but these fields can be overriden below if affected by an action. */
     switchElem.flowType.sw.dst_vlan = switchElem.flowType.sw.src_vlan;
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index 9e287e7..f604367 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -2779,16 +2779,12 @@ xlate_set_queue_action(struct action_xlate_ctx *ctx,
 static void
 xlate_set_dl_tci(struct action_xlate_ctx *ctx)
 {
-    ovs_be16 dl_vlan = ctx->flow.dl_vlan;
-    uint8_t dl_vlan_pcp = ctx->flow.dl_vlan_pcp;
-
-    if (dl_vlan == htons(OFP_VLAN_NONE)) {
+    ovs_be16 tci = ctx->flow.vlan_tci;
+    if (!(tci & htons(VLAN_CFI))) {
         odp_actions_add(ctx->out, ODPAT_STRIP_VLAN);
     } else {
         union odp_action *oa = odp_actions_add(ctx->out, ODPAT_SET_DL_TCI);
-        oa->dl_tci.tci = htons(ntohs(dl_vlan & htons(VLAN_VID_MASK))
-                               | (dl_vlan_pcp << VLAN_PCP_SHIFT)
-                               | VLAN_CFI);
+        oa->dl_tci.tci = tci & ~htons(VLAN_CFI);
     }
 }
 
@@ -2796,12 +2792,11 @@ static void
 xlate_reg_move_action(struct action_xlate_ctx *ctx,
                       const struct nx_action_reg_move *narm)
 {
-    ovs_be16 old_vlan = ctx->flow.dl_vlan;
-    uint8_t old_pcp = ctx->flow.dl_vlan_pcp;
+    ovs_be16 old_tci = ctx->flow.vlan_tci;
 
     nxm_execute_reg_move(narm, &ctx->flow);
 
-    if (ctx->flow.dl_vlan != old_vlan || ctx->flow.dl_vlan_pcp != old_pcp) {
+    if (ctx->flow.vlan_tci != old_tci) {
         xlate_set_dl_tci(ctx);
     }
 }
@@ -2888,18 +2883,20 @@ do_xlate_actions(const union ofp_action *in, size_t n_in,
             break;
 
         case OFPAT_SET_VLAN_VID:
-            ctx->flow.dl_vlan = ia->vlan_vid.vlan_vid;
+            ctx->flow.vlan_tci &= ~htons(VLAN_VID_MASK);
+            ctx->flow.vlan_tci |= ia->vlan_vid.vlan_vid | htons(VLAN_CFI);
             xlate_set_dl_tci(ctx);
             break;
 
         case OFPAT_SET_VLAN_PCP:
-            ctx->flow.dl_vlan_pcp = ia->vlan_pcp.vlan_pcp;
+            ctx->flow.vlan_tci &= ~htons(VLAN_PCP_MASK);
+            ctx->flow.vlan_tci |= htons(
+                (ia->vlan_pcp.vlan_pcp << VLAN_PCP_SHIFT) | VLAN_CFI);
             xlate_set_dl_tci(ctx);
             break;
 
         case OFPAT_STRIP_VLAN:
-            ctx->flow.dl_vlan = htons(OFP_VLAN_NONE);
-            ctx->flow.dl_vlan_pcp = 0;
+            ctx->flow.vlan_tci = htons(0);
             xlate_set_dl_tci(ctx);
             break;
 
diff --git a/tests/ovs-ofctl.at b/tests/ovs-ofctl.at
index 718249d..4318690 100644
--- a/tests/ovs-ofctl.at
+++ b/tests/ovs-ofctl.at
@@ -53,10 +53,10 @@ NXM_OF_VLAN_TCI(f009)
 NXM_OF_VLAN_TCI(f009) NXM_OF_VLAN_TCI(f009)
 NXM_OF_VLAN_TCI(0000)           # Packets without 802.1Q header.
 NXM_OF_VLAN_TCI(3123)           # Packets with VID=123, PCP=1.
-NXM_OF_VLAN_TCI(0123)           # Does not make sense.
+NXM_OF_VLAN_TCI(0123)           # Does not make sense (but supported anyway)
 NXM_OF_VLAN_TCI_W(1123/1fff)    # Packets with VID=123, any PCP.
 NXM_OF_VLAN_TCI_W(f000/f000)    # Packets with any VID, PCP=7.
-NXM_OF_VLAN_TCI_W(0000/e000)    # No 802.1Q or with VID=0 (not yet supported)
+NXM_OF_VLAN_TCI_W(0000/e000)    # No 802.1Q or with VID=0
 
 # IP TOS
 NXM_OF_ETH_TYPE(0800) NXM_OF_IP_TOS(f0)
@@ -131,8 +131,7 @@ NXM_NX_TUN_ID(00000000abcdef01)
 NXM_NX_REG0(acebdf56)
 NXM_NX_REG0_W(a0e0d050/f0f0f0f0)
 ])
-AT_CHECK([ovs-ofctl parse-nx-match < nx-match.txt], [0], [stdout])
-AT_CHECK([cat stdout], [0], [dnl
+AT_CHECK([ovs-ofctl parse-nx-match < nx-match.txt], [0], [dnl
 <any>
 
 # in port
@@ -158,10 +157,10 @@ NXM_OF_VLAN_TCI(f009)
 nx_pull_match() returned error 44010105
 NXM_OF_VLAN_TCI(0000)
 NXM_OF_VLAN_TCI(3123)
-nx_pull_match() returned error 44010100
+NXM_OF_VLAN_TCI(0123)
 NXM_OF_VLAN_TCI_W(1123/1fff)
 NXM_OF_VLAN_TCI_W(f000/f000)
-nx_pull_match() returned error 44010100
+NXM_OF_VLAN_TCI_W(0000/e000)
 
 # IP TOS
 NXM_OF_ETH_TYPE(0800), NXM_OF_IP_TOS(f0)
diff --git a/tests/test-classifier.c b/tests/test-classifier.c
index fc1470e..d78236d 100644
--- a/tests/test-classifier.c
+++ b/tests/test-classifier.c
@@ -48,14 +48,13 @@
     CLS_FIELD(0,                          nw_src,      NW_SRC)      \
     CLS_FIELD(0,                          nw_dst,      NW_DST)      \
     CLS_FIELD(FWW_IN_PORT,                in_port,     IN_PORT)     \
-    CLS_FIELD(FWW_DL_VLAN,                dl_vlan,     DL_VLAN)     \
+    CLS_FIELD(0,                          vlan_tci,    VLAN_TCI)    \
     CLS_FIELD(FWW_DL_TYPE,                dl_type,     DL_TYPE)     \
     CLS_FIELD(FWW_TP_SRC,                 tp_src,      TP_SRC)      \
     CLS_FIELD(FWW_TP_DST,                 tp_dst,      TP_DST)      \
     CLS_FIELD(FWW_DL_SRC,                 dl_src,      DL_SRC)      \
     CLS_FIELD(FWW_DL_DST | FWW_ETH_MCAST, dl_dst,      DL_DST)      \
     CLS_FIELD(FWW_NW_PROTO,               nw_proto,    NW_PROTO)    \
-    CLS_FIELD(FWW_DL_VLAN_PCP,            dl_vlan_pcp, DL_VLAN_PCP) \
     CLS_FIELD(FWW_NW_TOS,                 nw_tos,      NW_TOS)
 
 /* Field indexes.
@@ -199,6 +198,9 @@ match(const struct cls_rule *wild, const struct flow *fixed)
             eq = !((fixed->nw_src ^ wild->flow.nw_src) & wild->wc.nw_src_mask);
         } else if (f_idx == CLS_F_IDX_NW_DST) {
             eq = !((fixed->nw_dst ^ wild->flow.nw_dst) & wild->wc.nw_dst_mask);
+        } else if (f_idx == CLS_F_IDX_VLAN_TCI) {
+            eq = !((fixed->vlan_tci ^ wild->flow.vlan_tci)
+                   & wild->wc.vlan_tci_mask);
         } else {
             NOT_REACHED();
         }
@@ -246,8 +248,7 @@ static ovs_be32 nw_dst_values[] = { CONSTANT_HTONL(0xc0a80002),
                                     CONSTANT_HTONL(0xc0a04455) };
 static ovs_be32 tun_id_values[] = { 0, 0xffff0000 };
 static uint16_t in_port_values[] = { 1, ODPP_LOCAL };
-static ovs_be16 dl_vlan_values[] = { CONSTANT_HTONS(101), CONSTANT_HTONS(0) };
-static uint8_t dl_vlan_pcp_values[] = { 7, 0 };
+static ovs_be16 vlan_tci_values[] = { CONSTANT_HTONS(101), CONSTANT_HTONS(0) };
 static ovs_be16 dl_type_values[]
             = { CONSTANT_HTONS(ETH_TYPE_IP), CONSTANT_HTONS(ETH_TYPE_ARP) };
 static ovs_be16 tp_src_values[] = { CONSTANT_HTONS(49362),
@@ -271,11 +272,8 @@ init_values(void)
     values[CLS_F_IDX_IN_PORT][0] = &in_port_values[0];
     values[CLS_F_IDX_IN_PORT][1] = &in_port_values[1];
 
-    values[CLS_F_IDX_DL_VLAN][0] = &dl_vlan_values[0];
-    values[CLS_F_IDX_DL_VLAN][1] = &dl_vlan_values[1];
-
-    values[CLS_F_IDX_DL_VLAN_PCP][0] = &dl_vlan_pcp_values[0];
-    values[CLS_F_IDX_DL_VLAN_PCP][1] = &dl_vlan_pcp_values[1];
+    values[CLS_F_IDX_VLAN_TCI][0] = &vlan_tci_values[0];
+    values[CLS_F_IDX_VLAN_TCI][1] = &vlan_tci_values[1];
 
     values[CLS_F_IDX_DL_SRC][0] = dl_src_values[0];
     values[CLS_F_IDX_DL_SRC][1] = dl_src_values[1];
@@ -309,8 +307,7 @@ init_values(void)
 #define N_NW_DST_VALUES ARRAY_SIZE(nw_dst_values)
 #define N_TUN_ID_VALUES ARRAY_SIZE(tun_id_values)
 #define N_IN_PORT_VALUES ARRAY_SIZE(in_port_values)
-#define N_DL_VLAN_VALUES ARRAY_SIZE(dl_vlan_values)
-#define N_DL_VLAN_PCP_VALUES ARRAY_SIZE(dl_vlan_pcp_values)
+#define N_VLAN_TCI_VALUES ARRAY_SIZE(vlan_tci_values)
 #define N_DL_TYPE_VALUES ARRAY_SIZE(dl_type_values)
 #define N_TP_SRC_VALUES ARRAY_SIZE(tp_src_values)
 #define N_TP_DST_VALUES ARRAY_SIZE(tp_dst_values)
@@ -323,8 +320,7 @@ init_values(void)
                        N_NW_DST_VALUES *        \
                        N_TUN_ID_VALUES *        \
                        N_IN_PORT_VALUES *       \
-                       N_DL_VLAN_VALUES *       \
-                       N_DL_VLAN_PCP_VALUES *   \
+                       N_VLAN_TCI_VALUES *       \
                        N_DL_TYPE_VALUES *       \
                        N_TP_SRC_VALUES *        \
                        N_TP_DST_VALUES *        \
@@ -358,9 +354,7 @@ compare_classifiers(struct classifier *cls, struct tcls *tcls)
         flow.nw_dst = nw_dst_values[get_value(&x, N_NW_DST_VALUES)];
         flow.tun_id = tun_id_values[get_value(&x, N_TUN_ID_VALUES)];
         flow.in_port = in_port_values[get_value(&x, N_IN_PORT_VALUES)];
-        flow.dl_vlan = dl_vlan_values[get_value(&x, N_DL_VLAN_VALUES)];
-        flow.dl_vlan_pcp = dl_vlan_pcp_values[get_value(&x,
-                N_DL_VLAN_PCP_VALUES)];
+        flow.vlan_tci = vlan_tci_values[get_value(&x, N_VLAN_TCI_VALUES)];
         flow.dl_type = dl_type_values[get_value(&x, N_DL_TYPE_VALUES)];
         flow.tp_src = tp_src_values[get_value(&x, N_TP_SRC_VALUES)];
         flow.tp_dst = tp_dst_values[get_value(&x, N_TP_DST_VALUES)];
@@ -454,6 +448,8 @@ make_rule(int wc_fields, unsigned int priority, int value_pat)
             rule->cls_rule.wc.nw_src_mask = htonl(UINT32_MAX);
         } else if (f_idx == CLS_F_IDX_NW_DST) {
             rule->cls_rule.wc.nw_dst_mask = htonl(UINT32_MAX);
+        } else if (f_idx == CLS_F_IDX_VLAN_TCI) {
+            rule->cls_rule.wc.vlan_tci_mask = htons(UINT16_MAX);
         } else {
             NOT_REACHED();
         }
diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c
index 9ddafe1..670d6ba 100644
--- a/vswitchd/bridge.c
+++ b/vswitchd/bridge.c
@@ -2162,7 +2162,8 @@ set_dst(struct dst *p, const struct flow *flow,
 {
     p->vlan = (out_port->vlan >= 0 ? OFP_VLAN_NONE
               : in_port->vlan >= 0 ? in_port->vlan
-              : ntohs(flow->dl_vlan));
+              : flow->vlan_tci == 0 ? OFP_VLAN_NONE
+              : vlan_tci_to_vid(flow->vlan_tci));
     return choose_output_iface(out_port, flow->dl_src, &p->dp_ifidx, tags);
 }
 
@@ -2264,9 +2265,15 @@ compose_dsts(const struct bridge *br, const struct flow *flow, uint16_t vlan,
              struct dst dsts[], tag_type *tags, uint16_t *nf_output_iface)
 {
     mirror_mask_t mirrors = in_port->src_mirrors;
+    int flow_vlan;
     struct dst *dst = dsts;
     size_t i;
 
+    flow_vlan = vlan_tci_to_vid(flow->vlan_tci);
+    if (flow_vlan == 0) {
+        flow_vlan = OFP_VLAN_NONE;
+    }
+
     if (out_port == FLOOD_PORT) {
         /* XXX use ODP_FLOOD if no vlans or bonding. */
         /* XXX even better, define each VLAN as a datapath port group */
@@ -2302,7 +2309,6 @@ compose_dsts(const struct bridge *br, const struct flow *flow, uint16_t vlan,
                     if (port_includes_vlan(port, m->out_vlan)
                         && set_dst(dst, flow, in_port, port, tags))
                     {
-                        int flow_vlan;
 
                         if (port->vlan < 0) {
                             dst->vlan = m->out_vlan;
@@ -2317,10 +2323,6 @@ compose_dsts(const struct bridge *br, const struct flow *flow, uint16_t vlan,
                          * tagging tags place. This is necessary because
                          * dst->vlan is the final vlan, after removing implicit
                          * tags. */
-                        flow_vlan = ntohs(flow->dl_vlan);
-                        if (flow_vlan == 0) {
-                            flow_vlan = OFP_VLAN_NONE;
-                        }
                         if (port == in_port && dst->vlan == flow_vlan) {
                             /* Don't send out input port on same VLAN. */
                             continue;
@@ -2333,7 +2335,7 @@ compose_dsts(const struct bridge *br, const struct flow *flow, uint16_t vlan,
         mirrors &= mirrors - 1;
     }
 
-    partition_dsts(dsts, dst - dsts, ntohs(flow->dl_vlan));
+    partition_dsts(dsts, dst - dsts, flow_vlan);
     return dst - dsts;
 }
 
@@ -2362,7 +2364,10 @@ compose_actions(struct bridge *br, const struct flow *flow, uint16_t vlan,
     n_dsts = compose_dsts(br, flow, vlan, in_port, out_port, dsts, tags,
                           nf_output_iface);
 
-    cur_vlan = ntohs(flow->dl_vlan);
+    cur_vlan = vlan_tci_to_vid(flow->vlan_tci);
+    if (cur_vlan == 0) {
+        cur_vlan = OFP_VLAN_NONE;
+    }
     for (p = dsts; p < &dsts[n_dsts]; p++) {
         union odp_action *a;
         if (p->vlan != cur_vlan) {
@@ -2371,7 +2376,7 @@ compose_actions(struct bridge *br, const struct flow *flow, uint16_t vlan,
             } else {
                 a = odp_actions_add(actions, ODPAT_SET_DL_TCI);
                 a->dl_tci.tci = htons(p->vlan & VLAN_VID_MASK);
-                a->dl_tci.tci |= htons(flow->dl_vlan_pcp << VLAN_PCP_SHIFT);
+                a->dl_tci.tci |= flow->vlan_tci & htons(VLAN_PCP_MASK);
             }
             cur_vlan = p->vlan;
         }
@@ -2387,25 +2392,16 @@ compose_actions(struct bridge *br, const struct flow *flow, uint16_t vlan,
 static int flow_get_vlan(struct bridge *br, const struct flow *flow,
                          struct port *in_port, bool have_packet)
 {
-    /* Note that dl_vlan of 0 and of OFP_VLAN_NONE both mean that the packet
-     * belongs to VLAN 0, so we should treat both cases identically.  (In the
-     * former case, the packet has an 802.1Q header that specifies VLAN 0,
-     * presumably to allow a priority to be specified.  In the latter case, the
-     * packet does not have any 802.1Q header.) */
-    int vlan = ntohs(flow->dl_vlan);
-    if (vlan == OFP_VLAN_NONE) {
-        vlan = 0;
-    }
+    int vlan = vlan_tci_to_vid(flow->vlan_tci);
     if (in_port->vlan >= 0) {
         if (vlan) {
             /* XXX support double tagging? */
             if (have_packet) {
                 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-                VLOG_WARN_RL(&rl, "bridge %s: dropping VLAN %"PRIu16" tagged "
+                VLOG_WARN_RL(&rl, "bridge %s: dropping VLAN %d tagged "
                              "packet received on port %s configured with "
                              "implicit VLAN %"PRIu16,
-                             br->name, ntohs(flow->dl_vlan),
-                             in_port->name, in_port->vlan);
+                             br->name, vlan, in_port->name, in_port->vlan);
             }
             return -1;
         }
-- 
1.7.1





More information about the dev mailing list