[ovs-dev] [PATCH 2/7] Add new port VLAN mode "dot1q-tunnel"

Eric Garver e at erig.me
Wed Mar 1 22:48:00 UTC 2017


 - Example:
     ovs-vsctl set Port p1 vlan_mode=dot1q-tunnel tag=100
   Pushes another VLAN 100 header on packets (tagged and untagged) on
   ingress, and pops it on egress.
 - Customer VLAN check:
     ovs-vsctl set Port p1 vlan_mode=dot1q-tunnel tag=100 cvlans=10,20
   Only customer VLAN of 10 and 20 are allowed.

Co-authored-by: Xiao Liang <shaw.leon at gmail.com>
Signed-off-by: Xiao Liang <shaw.leon at gmail.com>
Signed-off-by: Eric Garver <e at erig.me>
---
 NEWS                         |   1 +
 ofproto/ofproto-dpif-xlate.c |  76 ++++++++++++++++++++++++++----
 ofproto/ofproto-dpif-xlate.h |   4 +-
 ofproto/ofproto-dpif.c       |  33 ++++++++++++-
 ofproto/ofproto.h            |   8 +++-
 tests/ofproto-dpif.at        | 110 +++++++++++++++++++++++++------------------
 vswitchd/bridge.c            |  27 ++++++++++-
 vswitchd/vswitch.ovsschema   |  16 +++++--
 vswitchd/vswitch.xml         |  31 ++++++++++++
 9 files changed, 242 insertions(+), 64 deletions(-)

diff --git a/NEWS b/NEWS
index ca95bd313198..bbd6c92dacc1 100644
--- a/NEWS
+++ b/NEWS
@@ -3,6 +3,7 @@ Post-v2.7.0
    - Tunnels:
      * Added support to set packet mark for tunnel endpoint using
        `egress_pkt_mark` OVSDB option.
+     * New dot1q-tunnel (CVLAN) type via 802.1ad support.
    - EMC insertion probability is reduced to 1% and is configurable via
      the new 'other_config:emc-insert-inv-prob' option.
    - ovs-ofctl:
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index 321b3ffc314a..53244f8f7685 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -127,9 +127,13 @@ struct xbundle {
     struct lacp *lacp;             /* LACP handle or null. */
 
     enum port_vlan_mode vlan_mode; /* VLAN mode. */
+    uint16_t qinq_ethtype;         /* Ethertype of dot1q-tunnel interface
+                                    * either 0x8100 or 0x88a8. */
     int vlan;                      /* -1=trunk port, else a 12-bit VLAN ID. */
     unsigned long *trunks;         /* Bitmap of trunked VLANs, if 'vlan' == -1.
                                     * NULL if all VLANs are trunked. */
+    unsigned long *cvlans;         /* Bitmap of allowed customer vlans,
+                                    * NULL if all VLANs are allowed */
     bool use_priority_tags;        /* Use 802.1p tag for frames in VLAN 0? */
     bool floodable;                /* No port has OFPUTIL_PC_NO_FLOOD set? */
     bool protected;                /* Protected port mode */
@@ -499,6 +503,7 @@ static bool input_vid_is_valid(const struct xlate_ctx *,
                                uint16_t vid, struct xbundle *);
 static void xvlan_copy(struct xvlan *dst, const struct xvlan *src);
 static void xvlan_pop(struct xvlan *src);
+static void xvlan_push_uninit(struct xvlan *src);
 static void xvlan_extract(const struct flow *, struct xvlan *);
 static void xvlan_put(struct flow *, const struct xvlan *);
 static void xvlan_input_translate(const struct xbundle *,
@@ -550,8 +555,8 @@ static void xlate_xbridge_set(struct xbridge *, struct dpif *,
                               const struct dpif_backer_support *);
 static void xlate_xbundle_set(struct xbundle *xbundle,
                               enum port_vlan_mode vlan_mode,
-                              int vlan,
-                              unsigned long *trunks,
+                              uint16_t qinq_ethtype, int vlan,
+                              unsigned long *trunks, unsigned long *cvlans,
                               bool use_priority_tags,
                               const struct bond *bond, const struct lacp *lacp,
                               bool floodable, bool protected);
@@ -857,8 +862,8 @@ xlate_xbridge_set(struct xbridge *xbridge,
 
 static void
 xlate_xbundle_set(struct xbundle *xbundle,
-                  enum port_vlan_mode vlan_mode,
-                  int vlan, unsigned long *trunks,
+                  enum port_vlan_mode vlan_mode, uint16_t qinq_ethtype,
+                  int vlan, unsigned long *trunks, unsigned long *cvlans,
                   bool use_priority_tags,
                   const struct bond *bond, const struct lacp *lacp,
                   bool floodable, bool protected)
@@ -866,8 +871,10 @@ xlate_xbundle_set(struct xbundle *xbundle,
     ovs_assert(xbundle->xbridge);
 
     xbundle->vlan_mode = vlan_mode;
+    xbundle->qinq_ethtype = qinq_ethtype;
     xbundle->vlan = vlan;
     xbundle->trunks = trunks;
+    xbundle->cvlans = cvlans;
     xbundle->use_priority_tags = use_priority_tags;
     xbundle->floodable = floodable;
     xbundle->protected = protected;
@@ -962,8 +969,8 @@ xlate_xbundle_copy(struct xbridge *xbridge, struct xbundle *xbundle)
     new_xbundle->name = xstrdup(xbundle->name);
     xlate_xbundle_init(new_xcfg, new_xbundle);
 
-    xlate_xbundle_set(new_xbundle, xbundle->vlan_mode,
-                      xbundle->vlan, xbundle->trunks,
+    xlate_xbundle_set(new_xbundle, xbundle->vlan_mode, xbundle->qinq_ethtype,
+                      xbundle->vlan, xbundle->trunks, xbundle->cvlans,
                       xbundle->use_priority_tags, xbundle->bond, xbundle->lacp,
                       xbundle->floodable, xbundle->protected);
     LIST_FOR_EACH (xport, bundle_node, &xbundle->xports) {
@@ -1157,8 +1164,8 @@ xlate_remove_ofproto(struct ofproto_dpif *ofproto)
 void
 xlate_bundle_set(struct ofproto_dpif *ofproto, struct ofbundle *ofbundle,
                  const char *name, enum port_vlan_mode vlan_mode,
-                 int vlan,
-                 unsigned long *trunks,
+                 uint16_t qinq_ethtype, int vlan,
+                 unsigned long *trunks, unsigned long *cvlans,
                  bool use_priority_tags,
                  const struct bond *bond, const struct lacp *lacp,
                  bool floodable, bool protected)
@@ -1179,7 +1186,7 @@ xlate_bundle_set(struct ofproto_dpif *ofproto, struct ofbundle *ofbundle,
     free(xbundle->name);
     xbundle->name = xstrdup(name);
 
-    xlate_xbundle_set(xbundle, vlan_mode, vlan, trunks,
+    xlate_xbundle_set(xbundle, vlan_mode, qinq_ethtype, vlan, trunks, cvlans,
                       use_priority_tags, bond, lacp, floodable, protected);
 }
 
@@ -1701,6 +1708,12 @@ xbundle_trunks_vlan(const struct xbundle *bundle, uint16_t vlan)
 }
 
 static bool
+xbundle_allows_cvlan(const struct xbundle *bundle, uint16_t vlan)
+{
+    return (!bundle->cvlans || bitmap_is_set(bundle->cvlans, vlan));
+}
+
+static bool
 xbundle_includes_vlan(const struct xbundle *xbundle, const struct xvlan *xvlan)
 {
     switch (xbundle->vlan_mode) {
@@ -1712,6 +1725,10 @@ xbundle_includes_vlan(const struct xbundle *xbundle, const struct xvlan *xvlan)
     case PORT_VLAN_NATIVE_TAGGED:
         return xbundle_trunks_vlan(xbundle, xvlan->v[0].vid);
 
+    case PORT_VLAN_DOT1Q_TUNNEL:
+        return xvlan->v[0].vid == xbundle->vlan &&
+               xbundle_allows_cvlan(xbundle, xvlan->v[1].vid);
+
     default:
         OVS_NOT_REACHED();
     }
@@ -1948,6 +1965,15 @@ input_vid_is_valid(const struct xlate_ctx *ctx,
         }
         return true;
 
+    case PORT_VLAN_DOT1Q_TUNNEL:
+        if (!xbundle_allows_cvlan(in_xbundle, vid)) {
+            xlate_report_error(ctx, "dropping VLAN %"PRIu16" packet received "
+                               "on port %s not configured for dot1q tunneling"
+                               "VLAN %"PRIu16, vid, in_xbundle->name, vid);
+            return false;
+        }
+        return true;
+
     default:
         OVS_NOT_REACHED();
     }
@@ -1968,6 +1994,13 @@ xvlan_pop(struct xvlan *src)
            sizeof(src->v[FLOW_MAX_VLAN_HEADERS - 1]));
 }
 
+static void
+xvlan_push_uninit(struct xvlan *src)
+{
+    memmove(&src->v[1], &src->v[0], sizeof(src->v) - sizeof(src->v[0]));
+    memset(&src->v[0], 0, sizeof(src->v[0]));
+}
+
 /* Extract VLAN information (headers) from flow */
 static void
 xvlan_extract(const struct flow *flow, struct xvlan *xvlan)
@@ -2035,6 +2068,14 @@ xvlan_input_translate(const struct xbundle *in_xbundle,
         }
         break;
 
+    case PORT_VLAN_DOT1Q_TUNNEL:
+        xvlan_copy(xvlan, in_xvlan);
+        xvlan_push_uninit(xvlan);
+        xvlan->v[0].tpid = in_xbundle->qinq_ethtype;
+        xvlan->v[0].vid = in_xbundle->vlan;
+        xvlan->v[0].pcp = 0;
+        break;
+
     default:
         OVS_NOT_REACHED();
     }
@@ -2064,11 +2105,26 @@ xvlan_output_translate(const struct xbundle *out_xbundle,
         }
         break;
 
+    case PORT_VLAN_DOT1Q_TUNNEL:
+        xvlan_copy(out_xvlan, xvlan);
+        xvlan_pop(out_xvlan);
+        break;
+
     default:
         OVS_NOT_REACHED();
     }
 }
 
+/* If output xbundle is dot1q-tunnel, set mask bits of cvlan */
+static void
+check_and_set_cvlan_mask(struct flow_wildcards *wc,
+                         const struct xbundle *xbundle)
+{
+    if (xbundle->vlan_mode == PORT_VLAN_DOT1Q_TUNNEL && xbundle->cvlans) {
+        wc->masks.vlans[1].tci = htons(0xffff);
+    }
+}
+
 static void
 output_normal(struct xlate_ctx *ctx, const struct xbundle *out_xbundle,
               const struct xvlan *xvlan)
@@ -2080,6 +2136,8 @@ output_normal(struct xlate_ctx *ctx, const struct xbundle *out_xbundle,
     bool use_recirc = false;
     struct xvlan out_xvlan;
 
+    check_and_set_cvlan_mask(ctx->wc, out_xbundle);
+
     xvlan_output_translate(out_xbundle, xvlan, &out_xvlan);
     if (out_xbundle->use_priority_tags) {
         out_xvlan.v[0].pcp = ntohs(ctx->xin->flow.vlans[0].tci) &
diff --git a/ofproto/ofproto-dpif-xlate.h b/ofproto/ofproto-dpif-xlate.h
index 708a19af0799..8a6bdf631600 100644
--- a/ofproto/ofproto-dpif-xlate.h
+++ b/ofproto/ofproto-dpif-xlate.h
@@ -150,8 +150,8 @@ void xlate_remove_ofproto(struct ofproto_dpif *);
 
 void xlate_bundle_set(struct ofproto_dpif *, struct ofbundle *,
                       const char *name, enum port_vlan_mode,
-                      int vlan,
-                      unsigned long *trunks,
+                      uint16_t qinq_ethtype, int vlan,
+                      unsigned long *trunks, unsigned long *cvlans,
                       bool use_priority_tags,
                       const struct bond *, const struct lacp *,
                       bool floodable, bool protected);
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index 7f1d60493b99..aaea15386e79 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -94,9 +94,11 @@ struct ofbundle {
     /* Configuration. */
     struct ovs_list ports;      /* Contains "struct ofport"s. */
     enum port_vlan_mode vlan_mode; /* VLAN mode */
+    uint16_t qinq_ethtype;
     int vlan;                   /* -1=trunk port, else a 12-bit VLAN ID. */
     unsigned long *trunks;      /* Bitmap of trunked VLANs, if 'vlan' == -1.
                                  * NULL if all VLANs are trunked. */
+    unsigned long *cvlans;
     struct lacp *lacp;          /* LACP if LACP is enabled, otherwise NULL. */
     struct bond *bond;          /* Nonnull iff more than one port. */
     bool use_priority_tags;     /* Use 802.1p tag for frames in VLAN 0? */
@@ -449,8 +451,8 @@ type_run(const char *type)
 
             HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) {
                 xlate_bundle_set(ofproto, bundle, bundle->name,
-                                 bundle->vlan_mode,
-                                 bundle->vlan, bundle->trunks,
+                                 bundle->vlan_mode, bundle->qinq_ethtype,
+                                 bundle->vlan, bundle->trunks, bundle->cvlans,
                                  bundle->use_priority_tags,
                                  bundle->bond, bundle->lacp,
                                  bundle->floodable, bundle->protected);
@@ -2796,6 +2798,7 @@ bundle_destroy(struct ofbundle *bundle)
     hmap_remove(&ofproto->bundles, &bundle->hmap_node);
     free(bundle->name);
     free(bundle->trunks);
+    free(bundle->cvlans);
     lacp_unref(bundle->lacp);
     bond_unref(bundle->bond);
     free(bundle);
@@ -2810,6 +2813,7 @@ bundle_set(struct ofproto *ofproto_, void *aux,
     struct ofport_dpif *port;
     struct ofbundle *bundle;
     unsigned long *trunks = NULL;
+    unsigned long *cvlans = NULL;
     int vlan;
     size_t i;
     bool ok;
@@ -2834,8 +2838,10 @@ bundle_set(struct ofproto *ofproto_, void *aux,
 
         ovs_list_init(&bundle->ports);
         bundle->vlan_mode = PORT_VLAN_TRUNK;
+        bundle->qinq_ethtype = ETH_TYPE_VLAN_8021AD;
         bundle->vlan = -1;
         bundle->trunks = NULL;
+        bundle->cvlans = NULL;
         bundle->use_priority_tags = s->use_priority_tags;
         bundle->lacp = NULL;
         bundle->bond = NULL;
@@ -2900,6 +2906,11 @@ bundle_set(struct ofproto *ofproto_, void *aux,
         need_flush = true;
     }
 
+    if (s->qinq_ethtype != bundle->qinq_ethtype) {
+        bundle->qinq_ethtype= s->qinq_ethtype;
+        need_flush = true;
+    }
+
     /* Set VLAN tag. */
     vlan = (s->vlan_mode == PORT_VLAN_TRUNK ? -1
             : s->vlan >= 0 && s->vlan <= 4095 ? s->vlan
@@ -2937,6 +2948,10 @@ bundle_set(struct ofproto *ofproto_, void *aux,
         }
         break;
 
+    case PORT_VLAN_DOT1Q_TUNNEL:
+        cvlans = CONST_CAST(unsigned long *, s->cvlans);
+        break;
+
     default:
         OVS_NOT_REACHED();
     }
@@ -2954,6 +2969,20 @@ bundle_set(struct ofproto *ofproto_, void *aux,
         free(trunks);
     }
 
+    if (!vlan_bitmap_equal(cvlans, bundle->cvlans)) {
+        free(bundle->cvlans);
+        if (cvlans== s->cvlans) {
+            bundle->cvlans = vlan_bitmap_clone(cvlans);
+        } else {
+            bundle->cvlans = cvlans;
+            cvlans = NULL;
+        }
+        need_flush = true;
+    }
+    if (cvlans != s->cvlans) {
+        free(cvlans);
+    }
+
     /* Bonding. */
     if (!ovs_list_is_short(&bundle->ports)) {
         bundle->ofproto->has_bonded_bundles = true;
diff --git a/ofproto/ofproto.h b/ofproto/ofproto.h
index 2b36eaf410a5..d44845ce3cf6 100644
--- a/ofproto/ofproto.h
+++ b/ofproto/ofproto.h
@@ -388,7 +388,11 @@ enum port_vlan_mode {
     /* Untagged incoming packets are part of 'vlan', as are incoming packets
      * tagged with 'vlan'.  Outgoing packets tagged with 'vlan' are untagged.
      * Other VLANs in 'trunks' are trunked. */
-    PORT_VLAN_NATIVE_UNTAGGED
+    PORT_VLAN_NATIVE_UNTAGGED,
+
+    /* 802.1q tunnel port. Incomming packets are added an outer vlan tag
+     * 'vlan'. If 'cvlans' is set, only allows VLANs in 'cvlans'. */
+    PORT_VLAN_DOT1Q_TUNNEL
 };
 
 /* Configuration of bundles. */
@@ -399,8 +403,10 @@ struct ofproto_bundle_settings {
     size_t n_slaves;
 
     enum port_vlan_mode vlan_mode; /* Selects mode for vlan and trunks */
+    uint16_t qinq_ethtype;
     int vlan;                   /* VLAN VID, except for PORT_VLAN_TRUNK. */
     unsigned long *trunks;      /* vlan_bitmap, except for PORT_VLAN_ACCESS. */
+    unsigned long *cvlans;
     bool use_priority_tags;     /* Use 802.1p tag for frames in VLAN 0? */
 
     struct bond_settings *bond; /* Must be nonnull iff if n_slaves > 1. */
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index 56adf8c9e992..af90a7ce2056 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -3182,6 +3182,11 @@ OVS_VSWITCHD_START(
    add-port br0 p7 vlan_mode=native-untagged tag=12              -- \
    add-port br0 p8 vlan_mode=native-untagged tag=12 trunks=10,12 \
                    other-config:priority-tags=true               -- \
+   add-port br0 p9 vlan_mode=dot1q-tunnel    tag=10              qinq_ethtype=802.1q  -- \
+   add-port br0 p10 vlan_mode=dot1q-tunnel   tag=10 cvlans=10,12 qinq_ethtype=802.1q  -- \
+   add-port br0 p11 vlan_mode=dot1q-tunnel   tag=12              qinq_ethtype=802.1q  -- \
+   add-port br0 p12 vlan_mode=dot1q-tunnel   tag=12              qinq_ethtype=802.1q  \
+                   other-config:priority-tags=true               -- \
    set Interface p1 type=dummy -- \
    set Interface p2 type=dummy -- \
    set Interface p3 type=dummy -- \
@@ -3189,7 +3194,11 @@ OVS_VSWITCHD_START(
    set Interface p5 type=dummy -- \
    set Interface p6 type=dummy -- \
    set Interface p7 type=dummy -- \
-   set Interface p8 type=dummy --])
+   set Interface p8 type=dummy -- \
+   set Interface p9 type=dummy -- \
+   set Interface p10 type=dummy -- \
+   set Interface p11 type=dummy -- \
+   set Interface p12 type=dummy --])
 
 dnl Each of these specifies an in_port by number, a VLAN VID (or "none"),
 dnl a VLAN PCP (used if the VID isn't "none") and the expected set of datapath
@@ -3198,84 +3207,93 @@ for tuple in \
         "100 none 0 drop" \
         "100 0    0 drop" \
         "100 0    1 drop" \
-        "100 10   0 1,5,6,7,8,pop_vlan,2" \
-        "100 10   1 1,5,6,7,8,pop_vlan,2" \
+        "100 10   0 1,5,6,7,8,pop_vlan,2,9" \
+        "100 10   1 1,5,6,7,8,pop_vlan,2,9" \
         "100 11   0 5,7" \
         "100 11   1 5,7" \
-        "100 12   0 1,5,6,pop_vlan,3,4,7,8" \
-        "100 12   1 1,5,6,pop_vlan,4,7,push_vlan(vid=0,pcp=1),3,8" \
+        "100 12   0 1,5,6,pop_vlan,3,4,7,8,11,12" \
+        "100 12   1 1,5,6,pop_vlan,4,7,11,push_vlan(vid=0,pcp=1),3,8,12" \
         "1  none 0 drop" \
         "1  0    0 drop" \
         "1  0    1 drop" \
-        "1  10   0 5,6,7,8,100,pop_vlan,2" \
-        "1  10   1 5,6,7,8,100,pop_vlan,2" \
+        "1  10   0 5,6,7,8,100,pop_vlan,2,9" \
+        "1  10   1 5,6,7,8,100,pop_vlan,2,9" \
         "1  11   0 drop" \
         "1  11   1 drop" \
-        "1  12   0 5,6,100,pop_vlan,3,4,7,8" \
-        "1  12   1 5,6,100,pop_vlan,4,7,push_vlan(vid=0,pcp=1),3,8" \
-        "2  none 0 push_vlan(vid=10,pcp=0),1,5,6,7,8,100" \
-        "2  0    0 pop_vlan,push_vlan(vid=10,pcp=0),1,5,6,7,8,100" \
-        "2  0    1 pop_vlan,push_vlan(vid=10,pcp=1),1,5,6,7,8,100" \
+        "1  12   0 5,6,100,pop_vlan,3,4,7,8,11,12" \
+        "1  12   1 5,6,100,pop_vlan,4,7,11,push_vlan(vid=0,pcp=1),3,8,12" \
+        "2  none 0 9,push_vlan(vid=10,pcp=0),1,5,6,7,8,100" \
+        "2  0    0 pop_vlan,9,push_vlan(vid=10,pcp=0),1,5,6,7,8,100" \
+        "2  0    1 pop_vlan,9,push_vlan(vid=10,pcp=1),1,5,6,7,8,100" \
         "2  10   0 drop" \
         "2  10   1 drop" \
         "2  11   0 drop" \
         "2  11   1 drop" \
         "2  12   0 drop" \
         "2  12   1 drop" \
-        "3  none 0 4,7,8,push_vlan(vid=12,pcp=0),1,5,6,100" \
-        "3  0    0 pop_vlan,4,7,8,push_vlan(vid=12,pcp=0),1,5,6,100" \
-        "3  0    1 8,pop_vlan,4,7,push_vlan(vid=12,pcp=1),1,5,6,100" \
+        "3  none 0 4,7,8,11,12,push_vlan(vid=12,pcp=0),1,5,6,100" \
+        "3  0    0 pop_vlan,4,7,8,11,12,push_vlan(vid=12,pcp=0),1,5,6,100" \
+        "3  0    1 8,12,pop_vlan,4,7,11,push_vlan(vid=12,pcp=1),1,5,6,100" \
         "3  10   0 drop" \
         "3  10   1 drop" \
         "3  11   0 drop" \
         "3  11   1 drop" \
         "3  12   0 drop" \
         "3  12   1 drop" \
-        "4  none 0 3,7,8,push_vlan(vid=12,pcp=0),1,5,6,100" \
-        "4  0    0 pop_vlan,3,7,8,push_vlan(vid=12,pcp=0),1,5,6,100" \
-        "4  0    1 3,8,pop_vlan,7,push_vlan(vid=12,pcp=1),1,5,6,100" \
+        "4  none 0 3,7,8,11,12,push_vlan(vid=12,pcp=0),1,5,6,100" \
+        "4  0    0 pop_vlan,3,7,8,11,12,push_vlan(vid=12,pcp=0),1,5,6,100" \
+        "4  0    1 3,8,12,pop_vlan,7,11,push_vlan(vid=12,pcp=1),1,5,6,100" \
         "4  10   0 drop" \
         "4  10   1 drop" \
         "4  11   0 drop" \
         "4  11   1 drop" \
         "4  12   0 drop" \
         "4  12   1 drop" \
-        "5  none 0 2,push_vlan(vid=10,pcp=0),1,6,7,8,100" \
-        "5  0    0 pop_vlan,2,push_vlan(vid=10,pcp=0),1,6,7,8,100" \
-        "5  0    1 pop_vlan,2,push_vlan(vid=10,pcp=1),1,6,7,8,100" \
-        "5  10   0 1,6,7,8,100,pop_vlan,2" \
-        "5  10   1 1,6,7,8,100,pop_vlan,2" \
+        "5  none 0 2,9,push_vlan(vid=10,pcp=0),1,6,7,8,100" \
+        "5  0    0 pop_vlan,2,9,push_vlan(vid=10,pcp=0),1,6,7,8,100" \
+        "5  0    1 pop_vlan,2,9,push_vlan(vid=10,pcp=1),1,6,7,8,100" \
+        "5  10   0 1,6,7,8,100,pop_vlan,2,9" \
+        "5  10   1 1,6,7,8,100,pop_vlan,2,9" \
         "5  11   0 7,100" \
         "5  11   1 7,100" \
-        "5  12   0 1,6,100,pop_vlan,3,4,7,8" \
-        "5  12   1 1,6,100,pop_vlan,4,7,push_vlan(vid=0,pcp=1),3,8" \
-        "6  none 0 2,push_vlan(vid=10,pcp=0),1,5,7,8,100" \
-        "6  0    0 pop_vlan,2,push_vlan(vid=10,pcp=0),1,5,7,8,100" \
-        "6  0    1 pop_vlan,2,push_vlan(vid=10,pcp=1),1,5,7,8,100" \
-        "6  10   0 1,5,7,8,100,pop_vlan,2" \
-        "6  10   1 1,5,7,8,100,pop_vlan,2" \
+        "5  12   0 1,6,100,pop_vlan,3,4,7,8,11,12" \
+        "5  12   1 1,6,100,pop_vlan,4,7,11,push_vlan(vid=0,pcp=1),3,8,12" \
+        "6  none 0 2,9,push_vlan(vid=10,pcp=0),1,5,7,8,100" \
+        "6  0    0 pop_vlan,2,9,push_vlan(vid=10,pcp=0),1,5,7,8,100" \
+        "6  0    1 pop_vlan,2,9,push_vlan(vid=10,pcp=1),1,5,7,8,100" \
+        "6  10   0 1,5,7,8,100,pop_vlan,2,9" \
+        "6  10   1 1,5,7,8,100,pop_vlan,2,9" \
         "6  11   0 drop" \
         "6  11   1 drop" \
-        "6  12   0 1,5,100,pop_vlan,3,4,7,8" \
-        "6  12   1 1,5,100,pop_vlan,4,7,push_vlan(vid=0,pcp=1),3,8" \
-        "7  none 0 3,4,8,push_vlan(vid=12,pcp=0),1,5,6,100" \
-        "7  0    0 pop_vlan,3,4,8,push_vlan(vid=12,pcp=0),1,5,6,100" \
-        "7  0    1 3,8,pop_vlan,4,push_vlan(vid=12,pcp=1),1,5,6,100" \
-        "7  10   0 1,5,6,8,100,pop_vlan,2" \
-        "7  10   1 1,5,6,8,100,pop_vlan,2" \
+        "6  12   0 1,5,100,pop_vlan,3,4,7,8,11,12" \
+        "6  12   1 1,5,100,pop_vlan,4,7,11,push_vlan(vid=0,pcp=1),3,8,12" \
+        "7  none 0 3,4,8,11,12,push_vlan(vid=12,pcp=0),1,5,6,100" \
+        "7  0    0 pop_vlan,3,4,8,11,12,push_vlan(vid=12,pcp=0),1,5,6,100" \
+        "7  0    1 3,8,12,pop_vlan,4,11,push_vlan(vid=12,pcp=1),1,5,6,100" \
+        "7  10   0 1,5,6,8,100,pop_vlan,2,9" \
+        "7  10   1 1,5,6,8,100,pop_vlan,2,9" \
         "7  11   0 5,100" \
         "7  11   1 5,100" \
-        "7  12   0 1,5,6,100,pop_vlan,3,4,8" \
-        "7  12   1 1,5,6,100,pop_vlan,4,push_vlan(vid=0,pcp=1),3,8" \
-        "8  none 0 3,4,7,push_vlan(vid=12,pcp=0),1,5,6,100" \
-        "8  0    0 pop_vlan,3,4,7,push_vlan(vid=12,pcp=0),1,5,6,100" \
-        "8  0    1 3,pop_vlan,4,7,push_vlan(vid=12,pcp=1),1,5,6,100" \
-        "8  10   0 1,5,6,7,100,pop_vlan,2" \
-        "8  10   1 1,5,6,7,100,pop_vlan,2" \
+        "7  12   0 1,5,6,100,pop_vlan,3,4,8,11,12" \
+        "7  12   1 1,5,6,100,pop_vlan,4,11,push_vlan(vid=0,pcp=1),3,8,12" \
+        "8  none 0 3,4,7,11,12,push_vlan(vid=12,pcp=0),1,5,6,100" \
+        "8  0    0 pop_vlan,3,4,7,11,12,push_vlan(vid=12,pcp=0),1,5,6,100" \
+        "8  0    1 3,12,pop_vlan,4,7,11,push_vlan(vid=12,pcp=1),1,5,6,100" \
+        "8  10   0 1,5,6,7,100,pop_vlan,2,9" \
+        "8  10   1 1,5,6,7,100,pop_vlan,2,9" \
         "8  11   0 drop" \
         "8  11   1 drop" \
-        "8  12   0 1,5,6,100,pop_vlan,3,4,7" \
-        "8  12   1 1,5,6,100,pop_vlan,4,7,push_vlan(vid=0,pcp=1),3"
+        "8  12   0 1,5,6,100,pop_vlan,3,4,7,11,12" \
+        "8  12   1 1,5,6,100,pop_vlan,4,7,11,push_vlan(vid=0,pcp=1),3,12" \
+        "9  none 0 2,push_vlan(vid=10,pcp=0),1,5,6,7,8,100" \
+        "9  10   0 10,push_vlan(vid=10,pcp=0),1,5,6,7,8,100" \
+        "9  11   0 push_vlan(vid=10,pcp=0),1,5,6,7,8,100" \
+        "10 none 0 drop" \
+        "10 0    0 drop" \
+        "10 11   0 drop" \
+        "10 12   0 9,push_vlan(vid=10,pcp=0),1,5,6,7,8,100" \
+        "11 10   0 7,8,12,push_vlan(vid=12,pcp=0),1,5,6,100" \
+        "11 10   1 7,8,12,push_vlan(vid=12,pcp=0),1,5,6,100"
 do
   set $tuple
   in_port=$1
diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c
index f1483112b46f..77fe686a51f2 100644
--- a/vswitchd/bridge.c
+++ b/vswitchd/bridge.c
@@ -978,6 +978,11 @@ port_configure(struct port *port)
         s.trunks = vlan_bitmap_from_array(cfg->trunks, cfg->n_trunks);
     }
 
+    s.cvlans = NULL;
+    if (cfg->n_cvlans) {
+        s.cvlans = vlan_bitmap_from_array(cfg->cvlans, cfg->n_cvlans);
+    }
+
     /* Get VLAN mode. */
     if (cfg->vlan_mode) {
         if (!strcmp(cfg->vlan_mode, "access")) {
@@ -988,6 +993,8 @@ port_configure(struct port *port)
             s.vlan_mode = PORT_VLAN_NATIVE_TAGGED;
         } else if (!strcmp(cfg->vlan_mode, "native-untagged")) {
             s.vlan_mode = PORT_VLAN_NATIVE_UNTAGGED;
+        } else if (!strcmp(cfg->vlan_mode, "dot1q-tunnel")) {
+            s.vlan_mode = PORT_VLAN_DOT1Q_TUNNEL;
         } else {
             /* This "can't happen" because ovsdb-server should prevent it. */
             VLOG_WARN("port %s: unknown VLAN mode %s, falling "
@@ -997,7 +1004,7 @@ port_configure(struct port *port)
     } else {
         if (s.vlan >= 0) {
             s.vlan_mode = PORT_VLAN_ACCESS;
-            if (cfg->n_trunks) {
+            if (cfg->n_trunks || cfg->n_cvlans) {
                 VLOG_WARN("port %s: ignoring trunks in favor of implicit vlan",
                           port->name);
             }
@@ -1005,6 +1012,24 @@ port_configure(struct port *port)
             s.vlan_mode = PORT_VLAN_TRUNK;
         }
     }
+
+    if (cfg->qinq_ethtype) {
+        if (!strcmp(cfg->qinq_ethtype, "802.1q") ||
+            !strcmp(cfg->qinq_ethtype, "0x8100")) {
+            s.qinq_ethtype = ETH_TYPE_VLAN_8021Q;
+        } else if (!strcmp(cfg->qinq_ethtype, "802.1ad") ||
+                   !strcmp(cfg->qinq_ethtype, "0x88a8")) {
+            s.qinq_ethtype = ETH_TYPE_VLAN_8021AD;
+        } else {
+            /* This "can't happen" because ovsdb-server should prevent it. */
+            VLOG_WARN("port %s: invalid QinQ ethertype %s, falling "
+                      "back to 802.1ad", port->name, cfg->qinq_ethtype);
+            s.qinq_ethtype = ETH_TYPE_VLAN_8021AD;
+        }
+    } else {
+        s.qinq_ethtype = ETH_TYPE_VLAN_8021AD;
+    }
+
     s.use_priority_tags = smap_get_bool(&cfg->other_config, "priority-tags",
                                         false);
 
diff --git a/vswitchd/vswitch.ovsschema b/vswitchd/vswitch.ovsschema
index de78dfd26d2c..c1804c847e1d 100644
--- a/vswitchd/vswitch.ovsschema
+++ b/vswitchd/vswitch.ovsschema
@@ -1,6 +1,6 @@
 {"name": "Open_vSwitch",
- "version": "7.15.0",
- "cksum": "2264024465 22987",
+ "version": "7.16.0",
+ "cksum": "125603819 23400",
  "tables": {
    "Open_vSwitch": {
      "columns": {
@@ -145,6 +145,11 @@
                           "minInteger": 0,
                           "maxInteger": 4095},
                   "min": 0, "max": 4096}},
+       "cvlans": {
+         "type": {"key": {"type": "integer",
+                          "minInteger": 0,
+                          "maxInteger": 4095},
+                  "min": 0, "max": 4096}},
        "tag": {
          "type": {"key": {"type": "integer",
                           "minInteger": 0,
@@ -152,7 +157,12 @@
                   "min": 0, "max": 1}},
        "vlan_mode": {
          "type": {"key": {"type": "string",
-           "enum": ["set", ["trunk", "access", "native-tagged", "native-untagged"]]},
+           "enum": ["set", ["trunk", "access", "native-tagged",
+                            "native-untagged", "dot1q-tunnel"]]},
+         "min": 0, "max": 1}},
+       "qinq_ethtype": {
+         "type": {"key": {"type": "string",
+           "enum": ["set", ["802.1q", "802.1ad", "0x88a8", "0x8100"]]},
          "min": 0, "max": 1}},
        "qos": {
          "type": {"key": {"type": "uuid",
diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml
index 14f5d14511fa..79b5462eb583 100644
--- a/vswitchd/vswitch.xml
+++ b/vswitchd/vswitch.xml
@@ -1326,6 +1326,22 @@
           exception that a packet that egresses on a native-untagged port in
           the native VLAN will not have an 802.1Q header.
         </dd>
+
+        <dt>dot1q-tunnel</dt>
+        <dd>
+          <p>
+            A dot1q-tunnel port tunnels packets with VLAN specified in
+            <ref column="tag"/> column. That is it adds 802.1Q header, with
+            ethertype and VLAN ID specified in <ref column="qinq-ethtype"/>
+            and <ref column="tag"/>, to both tagged and untagged packets on
+            ingress, and pops dot1q header of this VLAN on egress.
+          </p>
+
+          <p>
+            If <ref column="cvlans"/> is set, only allows packets of these
+            VLANs.
+          </p>
+        </dd>
       </dl>
       <p>
         A packet will only egress through bridge ports that carry the VLAN of
@@ -1349,6 +1365,13 @@
         </ul>
       </column>
 
+      <column name="qinq_ethtype">
+        <p>
+          Ethertype of dot1q-tunnel port, could be either "802.1q"(0x8100) or
+          "802.1ad"(0x88a8). Defaults to "802.1ad".
+        </p>
+      </column>
+
       <column name="tag">
         <p>
           For an access port, the port's implicitly tagged VLAN.  For a
@@ -1370,6 +1393,14 @@
         </p>
       </column>
 
+      <column name="cvlans">
+        <p>
+          For a trunk-qinq port if specific cvlans are specified only those
+          cvlans are 1ad tunneled, others are dropped. If no cvlans specified
+          explicitly then all cvlans are 1ad tunneled.
+        </p>
+      </column>
+
       <column name="other_config" key="priority-tags"
               type='{"type": "boolean"}'>
         <p>
-- 
2.10.0



More information about the dev mailing list