This is the Open vSwitch user space portion of the 802.1AD patch.

Signed-off-by: Thomas F Herbert <thomasfherb...@entpnt.com>

---
 NEWS                         |   2 +
 lib/flow.c                   |  22 +++--
 lib/flow.h                   |  10 +-
 lib/match.c                  |   2 +-
 lib/nx-match.c               |   2 +-
 lib/odp-execute.c            |   3 +-
 lib/odp-util.c               | 217 +++++++++++++++++++++++++++++++++++++------
 lib/odp-util.h               |   2 +-
 lib/ofp-actions.c            |  32 ++++---
 lib/ofp-actions.h            |  13 ++-
 lib/ofp-util.c               |   2 +-
 lib/packets.c                |   2 +-
 lib/packets.h                |   7 ++
 ofproto/ofproto-dpif-xlate.c |  13 ++-
 tests/ofproto-dpif.at        |  40 ++++++++
 utilities/ovs-ofctl.8.in     |   3 +-
 16 files changed, 307 insertions(+), 65 deletions(-)

diff --git a/NEWS b/NEWS
index f2fceb5..6655561 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,7 @@
 Post-v2.3.0
 ---------------------
+   - Add support for 8021.AD as specified in OpenFlow 1.1+. Adds push and pop 
+     of 802.1AD Ethertype and handling of QinQ or double stacked Vlans.
    - Add bash command-line completion support for ovs-appctl/ovs-dpctl/
      ovs-ofctl/ovsdb-tool commands.  Please check
      utilities/ovs-command-compgen.INSTALL.md for how to use.
diff --git a/lib/flow.c b/lib/flow.c
index eb7fdf1..da6599f 100644
--- a/lib/flow.c
+++ b/lib/flow.c
@@ -121,7 +121,7 @@ struct mf_ctx {
  * away.  Some GCC versions gave warnings on ALWAYS_INLINE, so these are
  * defined as macros. */
 
-#if (FLOW_WC_SEQ != 28)
+#if (FLOW_WC_SEQ != 29)
 #define MINIFLOW_ASSERT(X) ovs_assert(X)
 BUILD_MESSAGE("FLOW_WC_SEQ changed: miniflow_extract() will have runtime "
                "assertions enabled. Consider updating FLOW_WC_SEQ after "
@@ -228,7 +228,7 @@ parse_vlan(void **datap, size_t *sizep)
 
     data_pull(datap, sizep, ETH_ADDR_LEN * 2);
 
-    if (eth->eth_type == htons(ETH_TYPE_VLAN)) {
+    if (eth_type_vlan(eth->eth_type)) {
         if (OVS_LIKELY(*sizep
                        >= sizeof(struct qtag_prefix) + sizeof(ovs_be16))) {
             const struct qtag_prefix *qp = data_pull(datap, sizep, sizeof *qp);
@@ -689,7 +689,7 @@ flow_unwildcard_tp_ports(const struct flow *flow, struct 
flow_wildcards *wc)
 void
 flow_get_metadata(const struct flow *flow, struct flow_metadata *fmd)
 {
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 28);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 29);
 
     fmd->dp_hash = flow->dp_hash;
     fmd->recirc_id = flow->recirc_id;
@@ -836,7 +836,7 @@ void flow_wildcards_init_for_packet(struct flow_wildcards 
*wc,
     memset(&wc->masks, 0x0, sizeof wc->masks);
 
     /* Update this function whenever struct flow changes. */
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 28);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 29);
 
     if (flow->tunnel.ip_dst) {
         if (flow->tunnel.flags & FLOW_TNL_F_KEY) {
@@ -933,7 +933,7 @@ uint64_t
 flow_wc_map(const struct flow *flow)
 {
     /* Update this function whenever struct flow changes. */
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 28);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 29);
 
     uint64_t map = (flow->tunnel.ip_dst) ? MINIFLOW_MAP(tunnel) : 0;
 
@@ -985,7 +985,7 @@ void
 flow_wildcards_clear_non_packet_fields(struct flow_wildcards *wc)
 {
     /* Update this function whenever struct flow changes. */
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 28);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 29);
 
     memset(&wc->masks.metadata, 0, sizeof wc->masks.metadata);
     memset(&wc->masks.regs, 0, sizeof wc->masks.regs);
@@ -1546,7 +1546,7 @@ flow_push_mpls(struct flow *flow, int n, ovs_be16 
mpls_eth_type,
         flow->mpls_lse[0] = set_mpls_lse_values(ttl, tc, 1, htonl(label));
 
         /* Clear all L3 and L4 fields. */
-        BUILD_ASSERT(FLOW_WC_SEQ == 28);
+        BUILD_ASSERT(FLOW_WC_SEQ == 29);
         memset((char *) flow + FLOW_SEGMENT_2_ENDS_AT, 0,
                sizeof(struct flow) - FLOW_SEGMENT_2_ENDS_AT);
     }
@@ -1732,8 +1732,12 @@ flow_compose(struct ofpbuf *b, const struct flow *flow)
         return;
     }
 
-    if (flow->vlan_tci & htons(VLAN_CFI)) {
-        eth_push_vlan(b, htons(ETH_TYPE_VLAN), flow->vlan_tci);
+    if (flow->vlan_tci & htons(VLAN_CFI) &&
+        (flow->vlan_ctci & htons(VLAN_CFI))) {
+        eth_push_vlan(b, htons(ETH_TYPE_VLAN_8021Q), flow->vlan_ctci);
+        eth_push_vlan(b, htons(ETH_TYPE_VLAN_8021AD), flow->vlan_tci);
+    } else if (flow->vlan_tci & htons(VLAN_CFI)) {
+        eth_push_vlan(b, htons(ETH_TYPE_VLAN_8021Q), flow->vlan_tci);
     }
 
     if (flow->dl_type == htons(ETH_TYPE_IP)) {
diff --git a/lib/flow.h b/lib/flow.h
index 8e56d05..270cfd0 100644
--- a/lib/flow.h
+++ b/lib/flow.h
@@ -38,7 +38,7 @@ struct pkt_metadata;
 /* This sequence number should be incremented whenever anything involving flows
  * or the wildcarding of flows changes.  This will cause build assertion
  * failures in places which likely need to be updated. */
-#define FLOW_WC_SEQ 28
+#define FLOW_WC_SEQ 29
 
 /* Number of Open vSwitch extension 32-bit registers. */
 #define FLOW_N_REGS 8
@@ -109,7 +109,9 @@ struct flow {
     uint8_t dl_dst[ETH_ADDR_LEN]; /* Ethernet destination address. */
     uint8_t dl_src[ETH_ADDR_LEN]; /* Ethernet source address. */
     ovs_be16 dl_type;           /* Ethernet frame type. */
-    ovs_be16 vlan_tci;          /* If 802.1Q, TCI | VLAN_CFI; otherwise 0. */
+    ovs_be16 vlan_tci;          /* If 802.1Q, TCI | VLAN_CFI; If 802.1ad, 
outer tag */
+    ovs_be16 vlan_ctci;         /* If 802.1ad, Customer TCI | VLAN_CFI, inner 
tag */
+    ovs_be16 vlan_tpid;         /* Vlan protocol type, either 802.1ad or 
802.1q. */
     ovs_be32 mpls_lse[FLOW_MAX_MPLS_LABELS]; /* MPLS label stack entry. */
 
     /* L3 */
@@ -156,8 +158,8 @@ BUILD_ASSERT_DECL(sizeof(struct flow) % 4 == 0);
 
 /* Remember to update FLOW_WC_SEQ when changing 'struct flow'. */
 BUILD_ASSERT_DECL(offsetof(struct flow, dp_hash) + sizeof(uint32_t)
-                  == sizeof(struct flow_tnl) + 180
-                  && FLOW_WC_SEQ == 28);
+                  == sizeof(struct flow_tnl) + 184
+                  && FLOW_WC_SEQ == 29);
 
 /* Incremental points at which flow classification may be performed in
  * segments.
diff --git a/lib/match.c b/lib/match.c
index 480b972..c41073b 100644
--- a/lib/match.c
+++ b/lib/match.c
@@ -870,7 +870,7 @@ match_format(const struct match *match, struct ds *s, int 
priority)
 
     int i;
 
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 28);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 29);
 
     if (priority != OFP_DEFAULT_PRIORITY) {
         ds_put_format(s, "priority=%d,", priority);
diff --git a/lib/nx-match.c b/lib/nx-match.c
index 2ad3cf2..1f72a84 100644
--- a/lib/nx-match.c
+++ b/lib/nx-match.c
@@ -817,7 +817,7 @@ nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, const 
struct match *match,
     int match_len;
     int i;
 
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 28);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 29);
 
     /* Metadata. */
     if (match->wc.masks.dp_hash) {
diff --git a/lib/odp-execute.c b/lib/odp-execute.c
index 720d24e..521d3b9 100644
--- a/lib/odp-execute.c
+++ b/lib/odp-execute.c
@@ -473,11 +473,10 @@ odp_execute_actions(void *dp, struct dpif_packet 
**packets, int cnt, bool steal,
 
         case OVS_ACTION_ATTR_PUSH_VLAN: {
             const struct ovs_action_push_vlan *vlan = nl_attr_get(a);
-
             for (i = 0; i < cnt; i++) {
                 struct ofpbuf *buf = &packets[i]->ofpbuf;
 
-                eth_push_vlan(buf, htons(ETH_TYPE_VLAN), vlan->vlan_tci);
+                eth_push_vlan(buf, vlan->vlan_tpid, vlan->vlan_tci);
             }
             break;
         }
diff --git a/lib/odp-util.c b/lib/odp-util.c
index d52c172..06ddfaf 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -2861,7 +2861,7 @@ odp_flow_key_from_flow__(struct ofpbuf *buf, const struct 
flow *flow,
                          size_t max_mpls_depth, bool recirc, bool export_mask)
 {
     struct ovs_key_ethernet *eth_key;
-    size_t encap;
+    size_t encap = 0;
     const struct flow *data = export_mask ? mask : flow;
 
     nl_msg_put_u32(buf, OVS_KEY_ATTR_PRIORITY, data->skb_priority);
@@ -2887,11 +2887,19 @@ odp_flow_key_from_flow__(struct ofpbuf *buf, const 
struct flow *flow,
                                        sizeof *eth_key);
     get_ethernet_key(data, eth_key);
 
-    if (flow->vlan_tci != htons(0) || flow->dl_type == htons(ETH_TYPE_VLAN)) {
-        if (export_mask) {
-            nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, OVS_BE16_MAX);
+    if (flow->vlan_tci != htons(0) || eth_type_vlan(flow->dl_type)) {
+        if (flow->vlan_ctci != htons(0)) {
+            if (export_mask) {
+                nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, OVS_BE16_MAX);
+            } else {
+                nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, 
htons(ETH_TYPE_VLAN_8021AD));
+            }
         } else {
-            nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, htons(ETH_TYPE_VLAN));
+            if (export_mask) {
+                nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, OVS_BE16_MAX);
+            } else {
+                nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, 
htons(ETH_TYPE_VLAN_8021Q));
+            }
         }
         nl_msg_put_be16(buf, OVS_KEY_ATTR_VLAN, data->vlan_tci);
         encap = nl_msg_start_nested(buf, OVS_KEY_ATTR_ENCAP);
@@ -3624,6 +3632,81 @@ parse_8021q_onward(const struct nlattr 
*attrs[OVS_KEY_ATTR_MAX + 1],
     return MAX(fitness, encap_fitness);
 }
 
+/* Parse 802.1ADQ header then parse encapsulated Customer Vlan. */
+static enum odp_key_fitness
+parse_8021ad_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
+                    uint64_t present_attrs, int out_of_range_attr,
+                    uint64_t expected_attrs, struct flow *flow,
+                    const struct nlattr *key, size_t key_len,
+                    const struct flow *src_flow)
+{
+    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+    bool is_mask = src_flow != flow;
+
+    const struct nlattr *encap
+        = (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ENCAP)
+           ? attrs[OVS_KEY_ATTR_ENCAP] : NULL);
+    enum odp_key_fitness encap_fitness;
+    enum odp_key_fitness fitness;
+
+    /* Calculate fitness of outer attributes. */
+    if (!is_mask) {
+        expected_attrs |= ((UINT64_C(1) << OVS_KEY_ATTR_VLAN) |
+                          (UINT64_C(1) << OVS_KEY_ATTR_ENCAP));
+    } else {
+        if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_VLAN)) {
+            expected_attrs |= (UINT64_C(1) << OVS_KEY_ATTR_VLAN);
+        }
+        if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ENCAP)) {
+            expected_attrs |= (UINT64_C(1) << OVS_KEY_ATTR_ENCAP);
+        }
+    }
+    fitness = check_expectations(present_attrs, out_of_range_attr,
+                                 expected_attrs, key, key_len);
+
+    /* Set outertag, vlan_tci.
+     * Remove the TPID from dl_type since it's not the real Ethertype.  */
+    flow->dl_type = htons(0);
+    flow->vlan_tci = (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_VLAN)
+                      ? nl_attr_get_be16(attrs[OVS_KEY_ATTR_VLAN])
+                      : htons(0));
+    if (!is_mask) {
+        if (!(present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_VLAN))) {
+            return ODP_FIT_TOO_LITTLE;
+        } else if (flow->vlan_tci == htons(0)) {
+            /* Corner case for a truncated 802.1Q header. */
+            if (fitness == ODP_FIT_PERFECT && nl_attr_get_size(encap)) {
+                return ODP_FIT_TOO_MUCH;
+            }
+            return fitness;
+        } else if (!(flow->vlan_tci & htons(VLAN_CFI))) {
+            VLOG_ERR_RL(&rl, "Outer OVS_KEY_ATTR_VLAN 0x%04"PRIx16" is nonzero 
"
+                        "but CFI bit is not set", ntohs(flow->vlan_tci));
+            return ODP_FIT_ERROR;
+        }
+    } else {
+        if (!(present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ENCAP))) {
+            return fitness;
+        }
+    }
+
+    /* Now parse the encapsulated attributes. */
+    if (!parse_flow_nlattrs(nl_attr_get(encap), nl_attr_get_size(encap),
+                            attrs, &present_attrs, &out_of_range_attr)) {
+        return ODP_FIT_ERROR;
+    }
+    expected_attrs = 0;
+
+    if (!parse_ethertype(attrs, present_attrs, &expected_attrs, flow, 
src_flow)) {
+        return ODP_FIT_ERROR;
+    }
+    encap_fitness = parse_8021q_onward(attrs, present_attrs, out_of_range_attr,
+                                       expected_attrs, flow, key, key_len, 
src_flow);
+
+    /* The overall fitness is the worse of the outer and inner attributes. */
+    return MAX(fitness, encap_fitness);
+}
+
 static enum odp_key_fitness
 odp_flow_key_to_flow__(const struct nlattr *key, size_t key_len,
                        struct flow *flow, const struct flow *src_flow)
@@ -3699,7 +3782,7 @@ odp_flow_key_to_flow__(const struct nlattr *key, size_t 
key_len,
         expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ETHERNET;
     }
 
-    /* Get Ethertype or 802.1Q TPID or FLOW_DL_TYPE_NONE. */
+    /* Get Ethertype or VLAN TPID or FLOW_DL_TYPE_NONE. */
     if (!parse_ethertype(attrs, present_attrs, &expected_attrs, flow,
         src_flow)) {
         return ODP_FIT_ERROR;
@@ -3707,9 +3790,14 @@ odp_flow_key_to_flow__(const struct nlattr *key, size_t 
key_len,
 
     if (is_mask
         ? (src_flow->vlan_tci & htons(VLAN_CFI)) != 0
-        : src_flow->dl_type == htons(ETH_TYPE_VLAN)) {
-        return parse_8021q_onward(attrs, present_attrs, out_of_range_attr,
-                                  expected_attrs, flow, key, key_len, 
src_flow);
+        : eth_type_vlan(src_flow->dl_type)) {
+        if (src_flow->dl_type == ETH_TYPE_VLAN_8021AD) {
+            return parse_8021ad_onward(attrs, present_attrs, out_of_range_attr,
+                                       expected_attrs, flow, key, key_len, 
src_flow);
+        } else {
+            return parse_8021q_onward(attrs, present_attrs, out_of_range_attr,
+                                    expected_attrs, flow, key, key_len, 
src_flow);
+        }
     }
     if (is_mask) {
         flow->vlan_tci = htons(0xffff);
@@ -3947,37 +4035,114 @@ commit_set_ether_addr_action(const struct flow *flow, 
struct flow *base_flow,
         put_ethernet_key(&mask, &wc->masks);
     }
 }
-
+static int
+flow_get_vlan_depth(const struct flow *flow)
+{
+    int n = 0;
+    if (flow->vlan_tci & htons(VLAN_CFI))
+        n++;
+    if (flow->vlan_ctci & htons(VLAN_CFI))
+        n++;
+    return n;
+}
 static void
-pop_vlan(struct flow *base,
-         struct ofpbuf *odp_actions, struct flow_wildcards *wc)
+pop_vlan(struct flow *base, struct ofpbuf *odp_actions, struct flow_wildcards 
*wc)
 {
+    int base_n = flow_get_vlan_depth(base);
+
+    if (base_n == 0)
+        return;
+
     memset(&wc->masks.vlan_tci, 0xff, sizeof wc->masks.vlan_tci);
 
-    if (base->vlan_tci & htons(VLAN_CFI)) {
-        nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_VLAN);
+    if (base_n == 1) {
+
         base->vlan_tci = 0;
+        base->vlan_tpid = 0;
+        base->vlan_ctci = 0;
+
+    } else if (base_n ==2) {
+
+        base->vlan_tci = base->vlan_ctci;
+        base->vlan_ctci = 0;
+        base->vlan_tpid = htons(ETH_TYPE_VLAN_8021Q);
     }
+    nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_VLAN);
 }
 
 static void
-commit_vlan_action(ovs_be16 vlan_tci, struct flow *base,
-                   struct ofpbuf *odp_actions, struct flow_wildcards *wc)
+push_vlan(struct flow *base, ovs_be16 vlan_tci, ovs_be16 vlan_tpid,
+          struct ofpbuf *odp_actions, struct flow_wildcards *wc)
 {
-    if (base->vlan_tci == vlan_tci) {
+    struct ovs_action_push_vlan vlan;
+    int base_n;
+    ovs_be16 tpid = htons(ETH_TYPE_VLAN_8021Q);
+
+    if (!(vlan_tci & htons(VLAN_CFI)))
         return;
-    }
 
-    pop_vlan(base, odp_actions, wc);
-    if (vlan_tci & htons(VLAN_CFI)) {
-        struct ovs_action_push_vlan vlan;
+    memset(&wc->masks.vlan_tci, 0xff, sizeof wc->masks.vlan_tci);
+
 
-        vlan.vlan_tpid = htons(ETH_TYPE_VLAN);
-        vlan.vlan_tci = vlan_tci;
-        nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_PUSH_VLAN,
-                          &vlan, sizeof vlan);
+    base_n = flow_get_vlan_depth(base);
+
+    if (base_n == 2) {
+        return;
+    } else if (base_n == 1) {
+        if (!vlan_tpid) {
+            tpid = htons(ETH_TYPE_VLAN_8021AD);
+        } else {
+            tpid = vlan_tpid;
+        }
+        base->vlan_ctci = base->vlan_tci;
+    } else if (base_n == 0) {
+        if (!vlan_tpid) {
+            tpid = htons(ETH_TYPE_VLAN_8021Q);
+        } else {
+            tpid = vlan_tpid;
+        }
+        base->vlan_ctci = 0;
     }
     base->vlan_tci = vlan_tci;
+    base->vlan_tpid = tpid;
+
+    vlan.vlan_tpid = tpid;
+    vlan.vlan_tci = vlan_tci;
+    nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_PUSH_VLAN,
+                      &vlan, sizeof vlan);
+}
+
+static void
+commit_vlan_action(const struct flow *flow, struct flow *base,
+                   struct ofpbuf *odp_actions, struct flow_wildcards *wc)
+{
+    ovs_be16 vlan_tci = flow->vlan_tci;
+    ovs_be16 vlan_tpid = flow->vlan_tpid;
+    int base_n;
+    int flow_n;
+
+
+    if ((base->vlan_tci == vlan_tci) && (base->vlan_ctci == vlan_tci))
+        return;
+
+    base_n = flow_get_vlan_depth(base);
+    flow_n = flow_get_vlan_depth(flow);
+
+
+    if (flow_n == base_n) {
+        if (vlan_tci == base->vlan_tci) {
+            return;
+        } else {
+            pop_vlan(base, odp_actions, wc);
+            push_vlan(base, vlan_tci, vlan_tpid, odp_actions, wc);
+        }
+    }
+    else if (flow_n > base_n) {
+        push_vlan(base, vlan_tci, vlan_tpid, odp_actions, wc);
+    }
+    else if (flow_n < base_n) {
+        pop_vlan(base, odp_actions, wc);
+    }
 }
 
 /* Wildcarding already done at action translation time. */
@@ -4327,7 +4492,7 @@ commit_odp_actions(const struct flow *flow, struct flow 
*base,
     slow = commit_set_nw_action(flow, base, odp_actions, wc, use_masked);
     commit_set_port_action(flow, base, odp_actions, wc, use_masked);
     commit_mpls_action(flow, base, odp_actions);
-    commit_vlan_action(flow->vlan_tci, base, odp_actions, wc);
+    commit_vlan_action(flow, base, odp_actions, wc);
     commit_set_priority_action(flow, base, odp_actions, wc, use_masked);
     commit_set_pkt_mark_action(flow, base, odp_actions, wc, use_masked);
 
diff --git a/lib/odp-util.h b/lib/odp-util.h
index 9c990cd..b361795 100644
--- a/lib/odp-util.h
+++ b/lib/odp-util.h
@@ -133,7 +133,7 @@ void odp_portno_names_destroy(struct hmap *portno_names);
  * add another field and forget to adjust this value.
  */
 #define ODPUTIL_FLOW_KEY_BYTES 512
-BUILD_ASSERT_DECL(FLOW_WC_SEQ == 28);
+BUILD_ASSERT_DECL(FLOW_WC_SEQ == 29);
 
 /* A buffer with sufficient size and alignment to hold an nlattr-formatted flow
  * key.  An array of "struct nlattr" might not, in theory, be sufficiently
diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
index 4680d81..7414a78 100644
--- a/lib/ofp-actions.c
+++ b/lib/ofp-actions.c
@@ -1266,16 +1266,20 @@ format_STRIP_VLAN(const struct ofpact_null *a, struct 
ds *s)
 static enum ofperr
 decode_OFPAT_RAW11_PUSH_VLAN(ovs_be16 eth_type, struct ofpbuf *out)
 {
-    if (eth_type != htons(ETH_TYPE_VLAN_8021Q)) {
-        /* XXX 802.1AD(QinQ) isn't supported at the moment */
+    struct ofpact_push_vlan *oam;
+
+    if (!eth_type_vlan(eth_type)) {
+        /* XXX Only 802.1q and 802.1AD(QinQ) is supported.  */
         return OFPERR_OFPBAC_BAD_ARGUMENT;
     }
-    ofpact_put_PUSH_VLAN(out);
+    oam = ofpact_put_PUSH_VLAN(out);
+    oam->ethertype = eth_type;
+
     return 0;
 }
 
 static void
-encode_PUSH_VLAN(const struct ofpact_null *null OVS_UNUSED,
+encode_PUSH_VLAN(const struct ofpact_push_vlan *push_vlan,
                  enum ofp_version ofp_version, struct ofpbuf *out)
 {
     if (ofp_version == OFP10_VERSION) {
@@ -1283,7 +1287,7 @@ encode_PUSH_VLAN(const struct ofpact_null *null 
OVS_UNUSED,
          * follow this action. */
     } else {
         /* XXX ETH_TYPE_VLAN_8021AD case */
-        put_OFPAT11_PUSH_VLAN(out, htons(ETH_TYPE_VLAN_8021Q));
+        put_OFPAT11_PUSH_VLAN(out, push_vlan->ethertype);
     }
 }
 
@@ -1295,25 +1299,25 @@ parse_PUSH_VLAN(char *arg, struct ofpbuf *ofpacts,
     char *error;
 
     *usable_protocols &= OFPUTIL_P_OF11_UP;
-    error = str_to_u16(arg, "ethertype", &ethertype);
+    error = str_to_u16(arg, "push_vlan", &ethertype);
     if (error) {
         return error;
     }
 
-    if (ethertype != ETH_TYPE_VLAN_8021Q) {
-        /* XXX ETH_TYPE_VLAN_8021AD case isn't supported */
+    if (!eth_type_vlan(htons(ethertype))) {
+        /* Check for valid VLAN ethertypes */
         return xasprintf("%s: not a valid VLAN ethertype", arg);
     }
 
-    ofpact_put_PUSH_VLAN(ofpacts);
+    ofpact_put_PUSH_VLAN(ofpacts)->ethertype = htons(ethertype);
     return NULL;
 }
 
 static void
-format_PUSH_VLAN(const struct ofpact_null *a OVS_UNUSED, struct ds *s)
+format_PUSH_VLAN(const struct ofpact_push_vlan *a OVS_UNUSED, struct ds *s)
 {
     /* XXX 802.1AD case*/
-    ds_put_format(s, "push_vlan:%#"PRIx16, ETH_TYPE_VLAN_8021Q);
+    ds_put_format(s, "push_vlan:%#"PRIx16, ntohs(a->ethertype));
 }
 
 /* Action structure for OFPAT10_SET_DL_SRC/DST and OFPAT11_SET_DL_SRC/DST. */
@@ -5357,8 +5361,9 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, 
struct ofpact *a,
         return 0;
 
     case OFPACT_PUSH_VLAN:
-        if (flow->vlan_tci & htons(VLAN_CFI)) {
-            /* Multiple VLAN headers not supported. */
+        if (flow->vlan_tci & htons(VLAN_CFI) &&
+            flow->vlan_ctci & htons(VLAN_CFI)) {
+            /* More then 2 levels of VLAN headers not supported. */
             return OFPERR_OFPBAC_BAD_TAG;
         }
         /* Temporary mark that we have a vlan tag. */
@@ -5971,6 +5976,7 @@ ofpacts_get_meter(const struct ofpact ofpacts[], size_t 
ofpacts_len)
 
 /* Formatting ofpacts. */
 
+
 static void
 ofpact_format(const struct ofpact *a, struct ds *s)
 {
diff --git a/lib/ofp-actions.h b/lib/ofp-actions.h
index 8362aa8..215bbd4 100644
--- a/lib/ofp-actions.h
+++ b/lib/ofp-actions.h
@@ -66,7 +66,7 @@
     OFPACT(SET_VLAN_VID,    ofpact_vlan_vid,    ofpact, "set_vlan_vid") \
     OFPACT(SET_VLAN_PCP,    ofpact_vlan_pcp,    ofpact, "set_vlan_pcp") \
     OFPACT(STRIP_VLAN,      ofpact_null,        ofpact, "strip_vlan")   \
-    OFPACT(PUSH_VLAN,       ofpact_null,        ofpact, "push_vlan")    \
+    OFPACT(PUSH_VLAN,       ofpact_push_vlan,   ofpact, "push_vlan")    \
     OFPACT(SET_ETH_SRC,     ofpact_mac,         ofpact, "mod_dl_src")   \
     OFPACT(SET_ETH_DST,     ofpact_mac,         ofpact, "mod_dl_dst")   \
     OFPACT(SET_IPV4_SRC,    ofpact_ipv4,        ofpact, "mod_nw_src")   \
@@ -393,7 +393,7 @@ struct ofpact_set_field {
     union mf_value mask;
 };
 
-/* OFPACT_PUSH_VLAN/MPLS/PBB
+/* OFPACT_PUSH_MPLS
  *
  * Used for NXAST_PUSH_MPLS, OFPAT11_PUSH_MPLS. */
 struct ofpact_push_mpls {
@@ -401,9 +401,16 @@ struct ofpact_push_mpls {
     ovs_be16 ethertype;
 };
 
+/* OFPACT_PUSH_VLAN
+ *
+ * Used for OFPAT11_PUSH_VLAN. */
+struct ofpact_push_vlan {
+    struct ofpact ofpact;
+    ovs_be16 ethertype;
+};
 /* OFPACT_POP_MPLS
  *
- * Used for NXAST_POP_MPLS, OFPAT11_POP_MPLS.. */
+ * Used for NXAST_POP_MPLS, OFPAT11_POP_MPLS. */
 struct ofpact_pop_mpls {
     struct ofpact ofpact;
     ovs_be16 ethertype;
diff --git a/lib/ofp-util.c b/lib/ofp-util.c
index 3f588c2..53982d5 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -186,7 +186,7 @@ ofputil_netmask_to_wcbits(ovs_be32 netmask)
 void
 ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *wc)
 {
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 28);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 29);
 
     /* Initialize most of wc. */
     flow_wildcards_init_catchall(wc);
diff --git a/lib/packets.c b/lib/packets.c
index af99e3b..f5b4526 100644
--- a/lib/packets.c
+++ b/lib/packets.c
@@ -217,7 +217,7 @@ set_ethertype(struct ofpbuf *packet, ovs_be16 eth_type)
         return;
     }
 
-    if (eh->eth_type == htons(ETH_TYPE_VLAN)) {
+    if (eth_type_vlan(eh->eth_type)) {
         ovs_be16 *p;
         char *l2_5 = ofpbuf_l2_5(packet);
 
diff --git a/lib/packets.h b/lib/packets.h
index 65d2274..b6596bd 100644
--- a/lib/packets.h
+++ b/lib/packets.h
@@ -252,6 +252,13 @@ static inline bool eth_type_mpls(ovs_be16 eth_type)
         eth_type == htons(ETH_TYPE_MPLS_MCAST);
 }
 
+static inline bool eth_type_vlan(ovs_be16 eth_type)
+{
+    return eth_type == htons(ETH_TYPE_VLAN_8021Q) ||
+        eth_type == htons(ETH_TYPE_VLAN_8021AD);
+}
+
+
 /* Minimum value for an Ethernet type.  Values below this are IEEE 802.2 frame
  * lengths. */
 #define ETH_TYPE_MIN           0x600
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index ba0c0d8..97237db 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -2627,6 +2627,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t 
ofp_port,
     struct flow *flow = &ctx->xin->flow;
     struct flow_tnl flow_tnl;
     ovs_be16 flow_vlan_tci;
+    ovs_be16 flow_vlan_ctci;
     uint32_t flow_pkt_mark;
     uint8_t flow_nw_tos;
     odp_port_t out_port, odp_port;
@@ -2635,7 +2636,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t 
ofp_port,
 
     /* If 'struct flow' gets additional metadata, we'll need to zero it out
      * before traversing a patch port. */
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 28);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 29);
     memset(&flow_tnl, 0, sizeof flow_tnl);
 
     if (!xport) {
@@ -2732,6 +2733,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t 
ofp_port,
     }
 
     flow_vlan_tci = flow->vlan_tci;
+    flow_vlan_ctci = flow->vlan_ctci;
     flow_pkt_mark = flow->pkt_mark;
     flow_nw_tos = flow->nw_tos;
 
@@ -2851,6 +2853,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t 
ofp_port,
  out:
     /* Restore flow */
     flow->vlan_tci = flow_vlan_tci;
+    flow->vlan_ctci = flow_vlan_ctci;
     flow->pkt_mark = flow_pkt_mark;
     flow->nw_tos = flow_nw_tos;
 }
@@ -3877,6 +3880,14 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t 
ofpacts_len,
             /* XXX 802.1AD(QinQ) */
             memset(&wc->masks.vlan_tci, 0xff, sizeof wc->masks.vlan_tci);
             flow->vlan_tci = htons(VLAN_CFI);
+            if (ofpact_get_PUSH_VLAN(a)->ethertype == 
htons(ETH_TYPE_VLAN_8021AD)) {
+                memset(&wc->masks.vlan_ctci, 0xff, sizeof wc->masks.vlan_ctci);
+                flow->vlan_ctci |= htons(VLAN_CFI);
+                memset(&wc->masks.vlan_tpid, 0xff, sizeof wc->masks.vlan_tpid);
+                flow->vlan_tpid = htons(ETH_TYPE_VLAN_8021AD);
+            } else if (ofpact_get_PUSH_VLAN(a)->ethertype == 
htons(ETH_TYPE_VLAN_8021Q)) {
+                flow->vlan_tpid = htons(ETH_TYPE_VLAN_8021Q);
+            }
             break;
 
         case OFPACT_SET_ETH_SRC:
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index 64cfcd4..7c21485 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -1475,6 +1475,46 @@ NXST_FLOW reply:
 OVS_VSWITCHD_STOP
 AT_CLEANUP
 
+AT_SETUP([ofproto-dpif - VLAN with AD Ethertype])
+OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], [1], [2], [3], [4])
+
+AT_DATA([flows1.txt], [dnl
+table=0 in_port=1 dl_type=0x8100 actions=pop_vlan,output:2
+table=0 in_port=1 dl_type=0x88a8 actions=pop_vlan,output:3
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows1.txt])
+AT_DATA([flows2.txt], [dnl
+table=0 in_port=2 dl_type=0x0800 
actions=push_vlan:0x8100,mod_vlan_vid:9,output:1
+table=0 in_port=3 dl_type=0x0800 
actions=push_vlan:0x88a8,set_field:0x1006->vlan_tci,output:1
+table=0 in_port=4 dl_type=0x0800 
actions=push_vlan:0x88a8,mod_vlan_vid:11,output:1
+])
+AT_CHECK([ovs-ofctl -O OpenFlow11 add-flows br0 flows2.txt])
+
+AT_CHECK([ovs-appctl ofproto/trace br0 
'in_port=1,dl_dst=ff:ff:ff:ff:ff:ff,dl_type=0x8100,dl_vlan=9'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: pop_vlan,2
+])
+AT_CHECK([ovs-appctl ofproto/trace br0 
'in_port=1,dl_dst=ff:ff:ff:ff:ff:ff,dl_type=0x88a8,dl_vlan=9'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: pop_vlan,3
+])
+AT_CHECK([ovs-appctl ofproto/trace br0 
'in_port=2,dl_dst=ff:ff:ff:ff:ff:ff,dl_type=0x0800'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: push_vlan(vid=9,pcp=0),1
+])
+AT_CHECK([ovs-appctl ofproto/trace br0 
'in_port=3,dl_dst=ff:ff:ff:ff:ff:ff,dl_type=0x0800'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: push_vlan(tpid=0x88a8,vid=6,pcp=0),1
+])
+AT_CHECK([ovs-appctl ofproto/trace br0 
'in_port=4,dl_dst=ff:ff:ff:ff:ff:ff,dl_type=0x0800'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: push_vlan(tpid=0x88a8,vid=11,pcp=0),1
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
 AT_SETUP([ofproto-dpif - MPLS handling])
 OVS_VSWITCHD_START([dnl
    add-port br0 p1 -- set Interface p1 type=dummy
diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in
index 7ffbeaa..ea8ae2e 100644
--- a/utilities/ovs-ofctl.8.in
+++ b/utilities/ovs-ofctl.8.in
@@ -1276,8 +1276,7 @@ Strips the VLAN tag from a packet if it is present.
 .
 .IP \fBpush_vlan\fR:\fIethertype\fR
 Push a new VLAN tag onto the packet.  Ethertype is used as the the Ethertype
-for the tag. Only ethertype 0x8100 should be used. (0x88a8 which the spec
-allows isn't supported at the moment.)
+for the tag. Ethertypes 0x8100, 0x88a8, or 0x9100 may be used.
 A priority of zero and the tag of zero are used for the new tag.
 .
 .IP \fBpush_mpls\fR:\fIethertype\fR
-- 
1.8.3.2

_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to