[ovs-dev] Hybrid VLAN capability

Ken Ajiro ken-ajiro at xr.jp.nec.com
Fri Mar 13 06:55:27 UTC 2015


  Hello,
  (My email address had changed)

  Thank you for your explanation about this matter.  I understand and
  agree that we should implement Egress Tables on OVS and use it
  instead of hybrid VLAN.
  (However unfortunately, I have currently no plan to implement Egress Tables)

  Although it might be not needed for OVS, just for your information,
  I attached my hybrid VLAN patch for OVS 2.3.1.  This patch will enable
  egress filtering in the environment which supports under 1.5 OpenFlow only.
  Of course, Egress Tables is more fine solution in OF 1.5+ environment.

  Regards,
  Ken  

 On Fri, 6 Mar 2015 10:22:26 -0800,
 jt at hpl.hp.com wrote
 in E-mail "Re: Hybrid VLAN capability":

> Ken Ajiro wrote :
> > 
> >   To isolate the L2 domain equivalent to VLAN,
> >   I think that following flow entries will be needed.
> > 
> > in_port=1,vlan_id=100,actions=output:2,output:7,output:8
> > in_port=1,vlan_id=110,actions=output:2,output:7,output:8
> > in_port=1,vlan_id=120,actions=output:2,output:7,output:8
> > in_port=1,drop
> > in_port=2,vlan_id=100,...
> 
> 	The way to avoid cross product explosion has always been to
> use multiple tables.
> 	Actually, Egress Tables introduced in 1.5 were explicitely
> designed to address this use case in a generic fashion, because Egress
> Tables have exactly the semantic you want. Instead of adding a table
> in the output port restricted/dedicated to only VLAN and nothing else,
> it introduce a generic OpenFlow table after the output action, so that
> not only you can do VLAN filtering, but you can also implement STP
> filtering, Reverse Path Filtering or any per-output port policies. In
> other words, it is future proof.
> 	In other words, please look at implementing Egress Tables in
> OVS instead of Hybrid Port VLAN. IMHO, I think Ben would be more
> likely to merge it. I think the current trend of trying to push more
> and more specialised/dedicated features in OVS is not sustainable.
> 	Regards,
> 
> 	Jean

 FAQ                          |   19 ++--
 ofproto/ofproto-dpif-xlate.c |  110 ++++++++++++++++++++--
 ofproto/ofproto-dpif-xlate.h |    1 +
 ofproto/ofproto-dpif.c       |   10 ++
 ofproto/ofproto.h            |    1 +
 tests/automake.mk            |    3 +-
 tests/hybrid-vlan.at         |  215 ++++++++++++++++++++++++++++++++++++++++++
 tests/testsuite.at           |    1 +
 vswitchd/bridge.c            |    3 +
 vswitchd/vswitch.xml         |   33 +++++++
 10 files changed, 377 insertions(+), 19 deletions(-)
 mode change 100644 => 100755 ofproto/ofproto-dpif-xlate.c
 mode change 100644 => 100755 ofproto/ofproto-dpif-xlate.h
 mode change 100644 => 100755 ofproto/ofproto-dpif.c
 mode change 100644 => 100755 ofproto/ofproto.h
 mode change 100644 => 100755 tests/automake.mk
 create mode 100755 tests/hybrid-vlan.at
 mode change 100644 => 100755 tests/testsuite.at
 mode change 100644 => 100755 vswitchd/bridge.c

diff --git a/FAQ b/FAQ
index 7ef7eb4..8a3ab83 100644
--- a/FAQ
+++ b/FAQ
@@ -1061,15 +1061,16 @@ A: RFC 1122 section 3.3.4.2 "Multihoming Requirements" describes two
 
 Q: My OpenFlow controller doesn't see the VLANs that I expect.
 
-A: The configuration for VLANs in the Open vSwitch database (e.g. via
-   ovs-vsctl) only affects traffic that goes through Open vSwitch's
-   implementation of the OpenFlow "normal switching" action.  By
-   default, when Open vSwitch isn't connected to a controller and
-   nothing has been manually configured in the flow table, all traffic
-   goes through the "normal switching" action.  But, if you set up
-   OpenFlow flows on your own, through a controller or using ovs-ofctl
-   or through other means, then you have to implement VLAN handling
-   yourself.
+A: Unless you are enabling "hybrid VLAN" (see the ovs-vswitchd.conf.db(5)
+   for the details of hybrid VLAN), the configuration for VLANs in the
+   Open vSwitch database (e.g. via ovs-vsctl) only affects traffic
+   that goes through Open vSwitch's implementation of the OpenFlow
+   "normal switching" action.  By default, when Open vSwitch isn't
+   connected to a controller and nothing has been manually configured
+   in the flow table, all traffic goes through the "normal switching"
+   action.  But, if you set up OpenFlow flows on your own, through a
+   controller or using ovs-ofctl or through other means, then you have
+   to implement VLAN handling yourself.
 
    You can use "normal switching" as a component of your OpenFlow
    actions, e.g. by putting "normal" into the lists of actions on
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
old mode 100644
new mode 100755
index 4f77ac5..f0edfb0
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -123,6 +123,7 @@ struct xbundle {
     unsigned long *trunks;         /* Bitmap of trunked VLANs, if 'vlan' == -1.
                                     * NULL if all VLANs are trunked. */
     bool use_priority_tags;        /* Use 802.1p tag for frames in VLAN 0? */
+    bool use_hybrid_vlan;          /* Use hybrid port VLAN mode? */
     bool floodable;                /* No port has OFPUTIL_PC_NO_FLOOD set? */
 };
 
@@ -196,6 +197,7 @@ struct xlate_ctx {
     bool use_recirc;            /* Should generate recirc? */
     struct xlate_recirc recirc; /* Information used for generating
                                  * recirculation actions */
+    ovs_be16 orig_vlan_tci;     /* VLAN TCI when packet arrived. */
 
     /* OpenFlow 1.1+ action set.
      *
@@ -310,6 +312,8 @@ static void xlate_table_action(struct xlate_ctx *, ofp_port_t in_port,
                                bool honor_table_miss);
 static bool input_vid_is_valid(uint16_t vid, struct xbundle *, bool warn);
 static uint16_t input_vid_to_vlan(const struct xbundle *, uint16_t vid);
+static ovs_be16 move_to_vlan(uint16_t vid, ovs_be16 orig_tci,
+                             bool use_priority_tags);
 static void output_normal(struct xlate_ctx *, const struct xbundle *,
                           uint16_t vlan);
 static void compose_output_action(struct xlate_ctx *, ofp_port_t ofp_port);
@@ -327,6 +331,10 @@ static bool dscp_from_skb_priority(const struct xport *, uint32_t skb_priority,
 static struct xc_entry *xlate_cache_add_entry(struct xlate_cache *xc,
                                               enum xc_type type);
 
+static bool hybrid_portvlan_adjust_action(const struct xbridge *xbridge,
+                                          ovs_be16 orig_tci, struct flow *flow,
+                                          ofp_port_t out_port);
+
 void
 xlate_ofproto_set(struct ofproto_dpif *ofproto, const char *name,
                   struct dpif *dpif, struct rule_dpif *miss_rule,
@@ -430,6 +438,7 @@ 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, bool use_priority_tags,
+                 bool use_hybrid_vlan,
                  const struct bond *bond, const struct lacp *lacp,
                  bool floodable)
 {
@@ -454,6 +463,7 @@ xlate_bundle_set(struct ofproto_dpif *ofproto, struct ofbundle *ofbundle,
     xbundle->vlan = vlan;
     xbundle->trunks = trunks;
     xbundle->use_priority_tags = use_priority_tags;
+    xbundle->use_hybrid_vlan = use_hybrid_vlan;
     xbundle->floodable = floodable;
 
     if (xbundle->bond != bond) {
@@ -1175,6 +1185,25 @@ input_vid_is_valid(uint16_t vid, struct xbundle *in_xbundle, bool warn)
 
 }
 
+/* Make 'orig_tci' moved to other VLAN.
+ * 'vid', in the range 0...4095, is the VID that 'orig_tci' will be moved to.
+ * If 'vid' is 0, vlan_tci will be made as no 802.1Q unless
+ * use_priority_tags is true and orig_tci has non zero priority (802.1p). */
+static ovs_be16
+move_to_vlan(uint16_t vid, ovs_be16 orig_tci, bool use_priority_tags)
+{
+    ovs_be16 tci;
+
+    tci = htons(vid);
+    if (tci || use_priority_tags) {
+        tci |= orig_tci & htons(VLAN_PCP_MASK);
+        if (tci) {
+            tci |= htons(VLAN_CFI);
+        }
+    }
+    return tci;
+}
+
 /* Given 'vlan', the VLAN that a packet belongs to, and
  * 'out_xbundle', a bundle on which the packet is to be output, returns the VID
  * that should be included in the 802.1Q header.  (If the return value is 0,
@@ -1207,7 +1236,7 @@ output_normal(struct xlate_ctx *ctx, const struct xbundle *out_xbundle,
 {
     ovs_be16 *flow_tci = &ctx->xin->flow.vlan_tci;
     uint16_t vid;
-    ovs_be16 tci, old_tci;
+    ovs_be16 old_tci;
     struct xport *xport;
 
     vid = output_vlan_to_vid(out_xbundle, vlan);
@@ -1267,14 +1296,7 @@ output_normal(struct xlate_ctx *ctx, const struct xbundle *out_xbundle,
     }
 
     old_tci = *flow_tci;
-    tci = htons(vid);
-    if (tci || out_xbundle->use_priority_tags) {
-        tci |= *flow_tci & htons(VLAN_PCP_MASK);
-        if (tci) {
-            tci |= htons(VLAN_CFI);
-        }
-    }
-    *flow_tci = tci;
+    *flow_tci = move_to_vlan(vid, *flow_tci, out_xbundle->use_priority_tags);
 
     compose_output_action(ctx, xport->ofp_port);
     *flow_tci = old_tci;
@@ -1936,6 +1958,12 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
     } else {
         odp_port = xport->odp_port;
         out_port = odp_port;
+
+        if (!hybrid_portvlan_adjust_action(ctx->xbridge, ctx->orig_vlan_tci,
+                                           flow, ofp_port)) {
+            goto out;
+        }
+
         if (ofproto_has_vlan_splinters(ctx->xbridge->ofproto)) {
             ofp_port_t vlandev_port;
 
@@ -3345,6 +3373,7 @@ xlate_actions__(struct xlate_in *xin, struct xlate_out *xout)
     ctx.table_id = 0;
     ctx.exit = false;
     ctx.use_recirc = false;
+    ctx.orig_vlan_tci = flow->vlan_tci;
 
     if (!xin->ofpacts && !ctx.rule) {
         ctx.table_id = rule_dpif_lookup(ctx.xbridge->ofproto, flow,
@@ -3752,3 +3781,66 @@ xlate_cache_delete(struct xlate_cache *xcache)
     ofpbuf_uninit(&xcache->entries);
     free(xcache);
 }
+
+static bool
+hybrid_portvlan_adjust_action(const struct xbridge *xbridge, ovs_be16 orig_tci,
+                              struct flow *flow, ofp_port_t out_port)
+{
+    ovs_be16 vlan_tci = flow->vlan_tci;
+    uint16_t vid;
+    struct xbundle *in_xbundle, *out_xbundle;
+    struct xport *xport;
+
+    in_xbundle = lookup_input_bundle(xbridge, flow->in_port.ofp_port,
+                                     false, NULL);
+    if (in_xbundle && in_xbundle->use_hybrid_vlan) {
+        /* Check Ingress VLAN. */
+        if (!input_vid_is_valid(vlan_tci_to_vid(orig_tci), in_xbundle, false)) {
+            return false;
+        }
+
+        /*
+         * Apply ingress port VLAN.  If VLAN modification action was
+         * already applied, that means ingress port VLAN was overwrote,
+         * so we don't apply port VLAN for such case.
+         */
+        if (vlan_tci == orig_tci) {
+            vid = input_vid_to_vlan(in_xbundle, vlan_tci_to_vid(orig_tci));
+            vlan_tci = move_to_vlan(vid, vlan_tci,
+                                    in_xbundle->use_priority_tags);
+        }
+    }
+
+    xport = get_ofp_port(xbridge, out_port);
+    out_xbundle = xport ? xport->xbundle : NULL;
+    if (out_xbundle && out_xbundle->use_hybrid_vlan) {
+        vid = vlan_tci_to_vid(vlan_tci);
+        if (!(vid == 0 && out_xbundle->vlan_mode != PORT_VLAN_TRUNK)
+            && !xbundle_includes_vlan(out_xbundle, vid)) {
+            /* VID is denied for the egress port */
+            return false;
+        }
+
+        /* Strip or push VLAN TCI for egress port VLAN */
+        switch (out_xbundle->vlan_mode) {
+        case PORT_VLAN_ACCESS:
+            vlan_tci = htons(0);
+            break;
+
+        case PORT_VLAN_TRUNK:
+        case PORT_VLAN_NATIVE_TAGGED:
+        case PORT_VLAN_NATIVE_UNTAGGED:
+            vid = output_vlan_to_vid(out_xbundle, vid);
+            vlan_tci = move_to_vlan(vid, vlan_tci,
+                                    out_xbundle->use_priority_tags);
+            break;
+
+        default:
+            OVS_NOT_REACHED();
+        }
+    }
+
+    flow->vlan_tci = vlan_tci;
+
+    return true;
+}
diff --git a/ofproto/ofproto-dpif-xlate.h b/ofproto/ofproto-dpif-xlate.h
old mode 100644
new mode 100755
index 760736a..a0afffc
--- a/ofproto/ofproto-dpif-xlate.h
+++ b/ofproto/ofproto-dpif-xlate.h
@@ -154,6 +154,7 @@ void xlate_remove_ofproto(struct ofproto_dpif *) OVS_REQ_WRLOCK(xlate_rwlock);
 void xlate_bundle_set(struct ofproto_dpif *, struct ofbundle *,
                       const char *name, enum port_vlan_mode, int vlan,
                       unsigned long *trunks, bool use_priority_tags,
+                      bool use_hybrid_vlan,
                       const struct bond *, const struct lacp *,
                       bool floodable) OVS_REQ_WRLOCK(xlate_rwlock);
 void xlate_bundle_remove(struct ofbundle *) OVS_REQ_WRLOCK(xlate_rwlock);
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
old mode 100644
new mode 100755
index 46595f8..a2dd065
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -125,6 +125,7 @@ struct ofbundle {
     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? */
+    bool use_hybrid_vlan;       /* Use hybrid port VLAN mode? */
 
     /* Status. */
     bool floodable;          /* True if no port has OFPUTIL_PC_NO_FLOOD set. */
@@ -605,6 +606,7 @@ type_run(const char *type)
                 xlate_bundle_set(ofproto, bundle, bundle->name,
                                  bundle->vlan_mode, bundle->vlan,
                                  bundle->trunks, bundle->use_priority_tags,
+                                 bundle->use_hybrid_vlan,
                                  bundle->bond, bundle->lacp,
                                  bundle->floodable);
             }
@@ -2328,6 +2330,7 @@ bundle_set(struct ofproto *ofproto_, void *aux,
         bundle->vlan = -1;
         bundle->trunks = NULL;
         bundle->use_priority_tags = s->use_priority_tags;
+        bundle->use_hybrid_vlan = s->use_hybrid_vlan;
         bundle->lacp = NULL;
         bundle->bond = NULL;
 
@@ -2444,6 +2447,12 @@ bundle_set(struct ofproto *ofproto_, void *aux,
         free(trunks);
     }
 
+    /* Set hybrid VLAN mode */
+    if (s->use_hybrid_vlan != bundle->use_hybrid_vlan) {
+        bundle->use_hybrid_vlan = s->use_hybrid_vlan;
+        need_flush = true;
+    }
+
     /* Bonding. */
     if (!list_is_short(&bundle->ports)) {
         bundle->ofproto->has_bonded_bundles = true;
diff --git a/ofproto/ofproto.h b/ofproto/ofproto.h
old mode 100644
new mode 100755
index 9a06849..6c0c734
--- a/ofproto/ofproto.h
+++ b/ofproto/ofproto.h
@@ -309,6 +309,7 @@ struct ofproto_bundle_settings {
     int vlan;                   /* VLAN VID, except for PORT_VLAN_TRUNK. */
     unsigned long *trunks;      /* vlan_bitmap, except for PORT_VLAN_ACCESS. */
     bool use_priority_tags;     /* Use 802.1p tag for frames in VLAN 0? */
+    bool use_hybrid_vlan;       /* Use hybrid port VLAN mode? */
 
     struct bond_settings *bond; /* Must be nonnull iff if n_slaves > 1. */
 
diff --git a/tests/automake.mk b/tests/automake.mk
old mode 100644
new mode 100755
index c115289..d529f52
--- a/tests/automake.mk
+++ b/tests/automake.mk
@@ -65,7 +65,8 @@ TESTSUITE_AT = \
 	tests/stp.at \
 	tests/interface-reconfigure.at \
 	tests/vlog.at \
-	tests/vtep-ctl.at
+	tests/vtep-ctl.at \
+	tests/hybrid-vlan.at
 TESTSUITE = $(srcdir)/tests/testsuite
 DISTCLEANFILES += tests/atconfig tests/atlocal
 
diff --git a/tests/hybrid-vlan.at b/tests/hybrid-vlan.at
new file mode 100755
index 0000000..c58f522
--- /dev/null
+++ b/tests/hybrid-vlan.at
@@ -0,0 +1,215 @@
+AT_BANNER([hybrid-vlan])
+
+m4_define([HYB_VLAN_SETUP],[
+# dnl
+# dnl   p01 trunk
+# dnl   p02 trunk
+# dnl   p11 access (tag=1)
+# dnl   p12 access (tag=1)
+# dnl   p21 access (tag=2)
+# dnl   p31 trunk  (trunks=1)
+# dnl   p32 trunk  (trunks=2)
+# dnl   p41 native-tagged   (tag=1, trunks=1)
+# dnl   p42 native-tagged   (tag=2, trunks=1)
+# dnl   p51 native-untagged (tag=1, trunks=1)
+# dnl   p52 native-untagged (tag=2, trunks=1)
+# dnl
+OVS_VSWITCHD_START(
+  [add-port br0 p01                                          -- set interface p01 ofport_request=1  type=dummy -- \
+   add-port br0 p02                                          -- set interface p02 ofport_request=2  type=dummy -- \
+   add-port br0 p11 tag=1                                    -- set interface p11 ofport_request=11 type=dummy -- \
+   add-port br0 p12 tag=1                                    -- set interface p12 ofport_request=12 type=dummy -- \
+   add-port br0 p21 tag=2                                    -- set interface p21 ofport_request=21 type=dummy -- \
+   add-port br0 p31 trunks=1                                 -- set interface p31 ofport_request=31 type=dummy -- \
+   add-port br0 p32 trunks=2                                 -- set interface p32 ofport_request=32 type=dummy -- \
+   add-port br0 p41 vlan_mode=native-tagged   tag=1 trunks=1 -- set interface p41 ofport_request=41 type=dummy -- \
+   add-port br0 p42 vlan_mode=native-tagged   tag=2 trunks=1 -- set interface p42 ofport_request=42 type=dummy -- \
+   add-port br0 p51 vlan_mode=native-untagged tag=1 trunks=1 -- set interface p51 ofport_request=51 type=dummy -- \
+   add-port br0 p52 vlan_mode=native-untagged tag=2 trunks=1 -- set interface p52 ofport_request=52 type=dummy -- \
+   add-port br0 p99 other_config:hybrid-vlan=false           -- set interface p99 ofport_request=99 type=dummy -- \
+   set Bridge br0 other_config:hybrid-vlan=true --])
+AT_CHECK([ovs-appctl vlog/set dpif:dbg])
+AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0 actions=1,2,11,12,controller,21,31,32,41,42,51,52,99])
+])
+
+m4_define([HYB_VLAN_CLEANUP],[
+OVS_VSWITCHD_STOP
+])
+
+AT_SETUP([hybrid-vlan - ingress tagged on trunk])
+HYB_VLAN_SETUP
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_vlan=1,dl_vlan_pcp=4'],
+         [0], [stdout])
+AT_CHECK([tail -3 stdout], [0], [dnl
+dnl
+dnl Access port for same VLAN (11, 12) should egress untagged frame.
+dnl Access port for differ VLAN (21) should not egress frame.
+dnl Trunk (native untagged) port that has the VLAN as native (51) should egress untagged frame.
+dnl Trunk port that trunks the VLAN (2, 31, 41, 42, 52) should egress tagged frame.
+dnl Trunk port that doesn't turnks the VLAN (32) should not egress frame.
+dnl Non hybrid trunk port (99) should egress tagged frame.
+dnl
+Datapath actions: 2,pop_vlan,11,12,push_vlan(vid=1,pcp=4),31,41,42,pop_vlan,51,push_vlan(vid=1,pcp=4),52,99
+This flow is handled by the userspace slow path because it:
+	- Sends "packet-in" messages to the OpenFlow controller.
+])
+HYB_VLAN_CLEANUP
+AT_CLEANUP
+
+AT_SETUP([hybrid-vlan - ingress untagged on trunk])
+HYB_VLAN_SETUP
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1'],
+         [0], [stdout])
+AT_CHECK([tail -3 stdout], [0], [dnl
+dnl
+dnl Access port (11, 12, 21) should egress untagged frame.
+dnl Trunk port that trunks the VLAN 0 (2) should egress tagged frame.
+dnl Trunk (native tagged/untagged) port (41, 42, 51, 52) should egress untagged frame.
+dnl Trunk port that doesn't turnks the VLAN (31, 32) should not egress frame.
+dnl Non hybrid trunk port (99) should egress untagged frame.
+dnl
+Datapath actions: 2,11,12,21,41,42,51,52,99
+This flow is handled by the userspace slow path because it:
+	- Sends "packet-in" messages to the OpenFlow controller.
+])
+HYB_VLAN_CLEANUP
+AT_CLEANUP
+
+AT_SETUP([hybrid-vlan - ingress untagged on trunk's native VLAN])
+HYB_VLAN_SETUP
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=51'], [0], [stdout])
+AT_CHECK([tail -3 stdout], [0], [dnl
+dnl
+dnl Access port for same VLAN (11, 12) should egress untagged frame.
+dnl Access port for differ VLAN (21) should not egress frame.
+dnl Trunk port that trunks the VLAN (1, 2, 31, 41, 42, 52) should egress tagged frame.
+dnl Trunk port that doesn't turnks the VLAN (32) should not egress frame.
+dnl Non hybrid trunk port (99) should egress tagged frame.
+dnl
+Datapath actions: push_vlan(vid=1,pcp=0),1,2,pop_vlan,11,12,push_vlan(vid=1,pcp=0),31,41,42,52,99
+This flow is handled by the userspace slow path because it:
+	- Sends "packet-in" messages to the OpenFlow controller.
+])
+HYB_VLAN_CLEANUP
+AT_CLEANUP
+
+AT_SETUP([hybrid-vlan - ingress tagged on access])
+HYB_VLAN_SETUP
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=11,dl_vlan=1'], [0], [stdout])
+AT_CHECK([tail -3 stdout], [0], [dnl
+dnl should be dropped on ingress.
+Datapath actions: drop
+This flow is handled by the userspace slow path because it:
+	- Sends "packet-in" messages to the OpenFlow controller.
+])
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=11,dl_vlan=2'], [0], [stdout])
+AT_CHECK([tail -3 stdout], [0], [dnl
+dnl should be dropped on ingress.
+Datapath actions: drop
+This flow is handled by the userspace slow path because it:
+	- Sends "packet-in" messages to the OpenFlow controller.
+])
+HYB_VLAN_CLEANUP
+AT_CLEANUP
+
+AT_SETUP([hybrid-vlan - ingress untagged on access])
+HYB_VLAN_SETUP
+AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=11'], [0], [stdout])
+AT_CHECK([tail -3 stdout], [0], [dnl
+dnl
+dnl Access port for same VLAN (12) should egress untagged frame.
+dnl Access port for differ VLAN (21) should not egress frame.
+dnl Trunk (native untagged) port that has the VLAN as native (51) should egress untagged frame.
+dnl Trunk port that trunks the VLAN (1, 2, 31, 41, 42, 52) should egress tagged frame.
+dnl Trunk port that doesn't turnks the VLAN (32) should not egress frame.
+dnl Non hybrid trunk port (99) should egress tagged frame.
+dnl
+Datapath actions: push_vlan(vid=1,pcp=0),1,2,pop_vlan,12,push_vlan(vid=1,pcp=0),31,41,42,pop_vlan,51,push_vlan(vid=1,pcp=0),52,99
+This flow is handled by the userspace slow path because it:
+	- Sends "packet-in" messages to the OpenFlow controller.
+])
+HYB_VLAN_CLEANUP
+AT_CLEANUP
+
+AT_SETUP([hybrid-vlan - Packet out untagged])
+HYB_VLAN_SETUP
+AT_CHECK([ovs-appctl ofproto/trace-packet-out br0 in_port=none '1,2,11,12,21,31,32,41,42,51,52,99'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0], [dnl
+dnl
+dnl Access port (11,12,21) should egress untagged frame.
+dnl Trunk port that trunks the VLAN 0 (1, 2) should egress tagged frame.
+dnl Trunk (native tagged/untagged) port (41, 42, 51, 52) should egress untagged frame.
+dnl Trunk port that doesn't turnks the VLAN (31, 32) should not egress frame.
+dnl Non hybrid trunk port (99) should egress tagged frame.
+dnl
+Datapath actions: 1,2,11,12,21,41,42,51,52,99
+])
+HYB_VLAN_CLEANUP
+AT_CLEANUP
+
+AT_SETUP([hybrid-vlan - Packet out tagged])
+HYB_VLAN_SETUP
+AT_CHECK([ovs-appctl ofproto/trace-packet-out br0 in_port=none,dl_vlan=1,dl_vlan_pcp=5 '1,2,11,12,21,31,32,41,42,51,52,99'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0], [dnl
+dnl
+dnl Access port for same VLAN (11, 12) should egress untagged frame.
+dnl Access port for differ VLAN (21) should not egress frame.
+dnl Trunk (native untagged) port that has the VLAN as native (51) should egress untagged frame.
+dnl Trunk port that trunks the VLAN (1, 2, 31, 41, 42, 52) should egress tagged frame.
+dnl Trunk port that doesn't turnks the VLAN (32) should not egress frame.
+dnl Non hybrid trunk port (99) should egress tagged frame.
+dnl
+Datapath actions: 1,2,pop_vlan,11,12,push_vlan(vid=1,pcp=5),31,41,42,pop_vlan,51,push_vlan(vid=1,pcp=5),52,99
+])
+HYB_VLAN_CLEANUP
+AT_CLEANUP
+
+AT_SETUP([hybrid-vlan - Packet out tagged from trunk])
+HYB_VLAN_SETUP
+AT_CHECK([ovs-appctl ofproto/trace-packet-out br0 in_port=1,dl_vlan=1,dl_vlan_pcp=4 '1,2,11,12,21,31,32,41,42,51,52,99'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0], [dnl
+dnl
+dnl Access port for same VLAN (11, 12) should egress untagged frame.
+dnl Access port for differ VLAN (21) should not egress frame.
+dnl Trunk (native untagged) port that has the VLAN as native (51) should egress untagged frame.
+dnl Trunk port that trunks the VLAN (2, 31, 41, 42, 52) should egress tagged frame.
+dnl Trunk port that doesn't turnks the VLAN (32) should not egress frame.
+dnl Non hybrid trunk port (99) should egress tagged frame.
+dnl
+Datapath actions: 2,pop_vlan,11,12,push_vlan(vid=1,pcp=4),31,41,42,pop_vlan,51,push_vlan(vid=1,pcp=4),52,99
+])
+HYB_VLAN_CLEANUP
+AT_CLEANUP
+
+AT_SETUP([hybrid-vlan - Packet In])
+HYB_VLAN_SETUP
+
+# Packet In tagged from trunk
+AT_CHECK([ovs-ofctl monitor br0 65534 --detach --no-chdir --pidfile 2> ofctl_monitor.log])
+AT_CHECK([ovs-appctl netdev-dummy/receive p01 'in_port(1),eth(src=40:44:44:44:00:01,dst=ff:ff:ff:ff:ff:ff),eth_type(0x8100),vlan(vid=2001,pcp=6),encap(eth_type(0x0806))'], [0], [stdout])
+sleep 1
+OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
+AT_CHECK([cat ofctl_monitor.log], [0], [dnl
+dnl
+dnl Original (== Tagged) frame should packet in
+dnl
+NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=60 in_port=1 (via action) data_len=60 (unbuffered)
+arp,metadata=0,in_port=0,dl_vlan=2001,dl_vlan_pcp=6,dl_src=40:44:44:44:00:01,dl_dst=ff:ff:ff:ff:ff:ff,arp_spa=0.0.0.0,arp_tpa=0.0.0.0,arp_sha=00:00:00:00:00:00,arp_tha=00:00:00:00:00:00
+])
+
+# Packet In from access
+AT_CHECK([ovs-ofctl monitor br0 65534 --detach --no-chdir --pidfile 2> ofctl_monitor.log])
+AT_CHECK([ovs-appctl netdev-dummy/receive p11 'in_port(11),eth(src=40:44:44:44:00:01,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806)'], [0], [stdout])
+sleep 1
+OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
+AT_CHECK([cat ofctl_monitor.log], [0], [dnl
+dnl
+dnl Original (== Unagged) frame should packet in
+dnl
+NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=60 in_port=11 (via action) data_len=60 (unbuffered)
+arp,metadata=0,in_port=0,vlan_tci=0x0000,dl_src=40:44:44:44:00:01,dl_dst=ff:ff:ff:ff:ff:ff,arp_spa=0.0.0.0,arp_tpa=0.0.0.0,arp_sha=00:00:00:00:00:00,arp_tha=00:00:00:00:00:00
+])
+
+HYB_VLAN_CLEANUP
+AT_CLEANUP
+
diff --git a/tests/testsuite.at b/tests/testsuite.at
old mode 100644
new mode 100755
index 9e23ebe..4cb7c22
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -157,3 +157,4 @@ m4_include([tests/interface-reconfigure.at])
 m4_include([tests/stp.at])
 m4_include([tests/vlog.at])
 m4_include([tests/vtep-ctl.at])
+m4_include([tests/hybrid-vlan.at])
diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c
old mode 100644
new mode 100755
index 6cd30b8..03a0a72
--- a/vswitchd/bridge.c
+++ b/vswitchd/bridge.c
@@ -909,6 +909,9 @@ port_configure(struct port *port)
     }
     s.use_priority_tags = smap_get_bool(&cfg->other_config, "priority-tags",
                                         false);
+    s.use_hybrid_vlan = smap_get_bool(&cfg->other_config, "hybrid-vlan",
+                            smap_get_bool(&port->bridge->cfg->other_config,
+                                          "hybrid-vlan", false));
 
     /* Get LACP settings. */
     s.lacp = port_configure_lacp(port, &lacp_settings);
diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml
index 79a2b5a..25d4311 100644
--- a/vswitchd/vswitch.xml
+++ b/vswitchd/vswitch.xml
@@ -759,6 +759,15 @@
           range, currently 10 to 1,000,000.
         </p>
       </column>
+
+      <column name="other_config" key="hybrid-vlan"
+              type='{"type": "boolean"}'>
+        <p>
+          Enable or disable "hybrid VLAN" (see the <ref table="Port"
+          column="other_config" key="hybrid-vlan"/> column in <ref
+          table="Port"/> table) on all ports included in this bridge.
+        </p>
+      </column>
     </group>
 
     <group title="Bridge Status">
@@ -940,6 +949,30 @@
           this setting is not meaningful on native-tagged ports.
         </p>
       </column>
+
+      <column name="other_config" key="hybrid-vlan"
+              type='{"type": "boolean"}'>
+        <p>
+          By default, VLAN configuration in this section only affects traffic
+          that goes through Open vSwitch's implementation of the OpenFlow
+          "normal switching" action.  However, if "hybrid VLAN" is enabled
+          on the port, VLAN configuration will also affect traffic controlled
+          by other OpenFlow action, e.g. OpenFlow output action.  Set this key
+          to <code>true</code> to enable hybrid VLAN on a port.
+        </p>
+
+        <p>
+          There is a column that has the same name, <ref table="Bridge"
+          column="other_config" key="hybrid-vlan"/>, in <ref table="Bridge"/>
+          table. If you set both of these columns, setting in <ref
+          table="Port"/> will override <ref table="Bridge"/>'s setting.
+        </p>
+
+        <p>
+          Please note that you should use OpenFlow "Egress-Tables" feature
+          instead of hybrid VLAN if OpenFlow have been implemented.
+        </p>
+      </column>
     </group>
 
     <group title="Bonding Configuration">
-- 
1.7.0.4


More information about the dev mailing list