[ovs-dev] [PATCH 2/4] lib: Userspace library changes to support provider VLANs

Avinash avinashandrew at gmail.com
Tue Jun 10 13:33:44 UTC 2014


This commit contains the following changes:
1. New vlan variables (tpid and count) are added to flow structure to support
   provider VLANs.
2. miniflow_extract() initialises these new variables based on received
   packet.
3. Altered flow sequence is modified in all the required places.
4. commit_vlan_action() is modified to support stacked VLANs

Signed-off-by: Avinash <avinashandrew at gmail.com>
---
 lib/flow.c     | 53 +++++++++++++++++++++++++++++++++++++++++++----------
 lib/flow.h     |  8 +++++---
 lib/match.c    |  2 +-
 lib/nx-match.c |  2 +-
 lib/odp-util.c | 39 ++++++++++++++++++++++++++++++---------
 lib/ofp-util.c |  2 +-
 lib/packets.c  |  2 +-
 lib/packets.h  | 16 ++++++++++++++++
 8 files changed, 98 insertions(+), 26 deletions(-)

diff --git a/lib/flow.c b/lib/flow.c
index 1f7f310..06e45ac 100644
--- a/lib/flow.c
+++ b/lib/flow.c
@@ -57,6 +57,11 @@ BUILD_ASSERT_DECL(offsetof(struct flow, dl_type) + 2
                   offsetof(struct flow, dl_type) / 4
                   == offsetof(struct flow, vlan_tci) / 4 );

+BUILD_ASSERT_DECL(offsetof(struct flow, vlan_tpid) + 2
+                  == offsetof(struct flow, vlan_count) &&
+                  offsetof(struct flow, vlan_tpid) / 4
+                  == offsetof(struct flow, vlan_count) / 4 );
+
 BUILD_ASSERT_DECL(offsetof(struct flow, nw_frag) + 3
                   == offsetof(struct flow, nw_proto) &&
                   offsetof(struct flow, nw_tos) + 2
@@ -121,7 +126,7 @@ struct mf_ctx {
  * away.  Some GCC versions gave warnigns on ALWAYS_INLINE, so these are
  * defined as macros. */

-#if (FLOW_WC_SEQ != 26)
+#if (FLOW_WC_SEQ != 27)
 #define MINIFLOW_ASSERT(X) ovs_assert(X)
 #else
 #define MINIFLOW_ASSERT(X)
@@ -214,21 +219,24 @@ parse_mpls(void **datap, size_t *sizep)
 }

 static inline ovs_be16
-parse_vlan(void **datap, size_t *sizep)
+parse_vlan(void **datap, size_t *sizep, ovs_be16 *tpid, uint8_t *vlan_count)
 {
     const struct eth_header *eth = *datap;

     struct qtag_prefix {
-        ovs_be16 eth_type;      /* ETH_TYPE_VLAN */
+        ovs_be16 eth_type;      /* ETH_TYPE_VLAN or ETH_TYPE_VLAN_8021AD */
         ovs_be16 tci;
     };

     data_pull(datap, sizep, ETH_ADDR_LEN * 2);
-
-    if (eth->eth_type == htons(ETH_TYPE_VLAN)) {
+    *tpid = htons(0);
+    *vlan_count = 0;
+    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);
+            *tpid = qp->eth_type;
+            *vlan_count = 1;
             return qp->tci | htons(VLAN_CFI);
         }
     }
@@ -268,6 +276,27 @@ parse_ethertype(void **datap, size_t *sizep)
     return htons(FLOW_DL_TYPE_NONE);
 }

+static inline ovs_be16
+parse_remaining_vlans(void **datap, size_t *sizep, uint8_t *vlan_count)
+{
+    ovs_be16 dl_type;
+    struct qtag_prefix {
+        ovs_be16 eth_type;      /* ETH_TYPE_VLAN or ETH_TYPE_VLAN_8021AD */
+        ovs_be16 tci;
+    };
+
+    dl_type = parse_ethertype(datap, sizep);
+    while (eth_type_vlan(dl_type)
+           && (OVS_LIKELY(*sizep
+                          >= sizeof(struct qtag_prefix) + sizeof(ovs_be16)))) {
+        const struct qtag_prefix *qp = data_pull(datap, sizep, sizeof *qp);
+        /* 'tci' field contains the actual ether type */
+        dl_type = qp->tci;
+        (*vlan_count)++;
+    }
+    return dl_type;
+}
+
 static inline bool
 parse_icmpv6(void **datap, size_t *sizep, const struct icmp6_hdr *icmp,
              const struct in6_addr **nd_target,
@@ -391,16 +420,20 @@ miniflow_extract(struct ofpbuf *packet, const
struct pkt_metadata *md,
         goto out;
     } else {
         ovs_be16 vlan_tci;
+        ovs_be16 vlan_tpid;
+        uint8_t vlan_count;

         /* Link layer. */
         BUILD_ASSERT(offsetof(struct flow, dl_dst) + 6
                      == offsetof(struct flow, dl_src));
         miniflow_push_words(mf, dl_dst, data, ETH_ADDR_LEN * 2 / 4);
         /* dl_type, vlan_tci. */
-        vlan_tci = parse_vlan(&data, &size);
-        dl_type = parse_ethertype(&data, &size);
+        vlan_tci = parse_vlan(&data, &size, &vlan_tpid, &vlan_count);
+        dl_type = parse_remaining_vlans(&data, &size, &vlan_count);
         miniflow_push_be16(mf, dl_type, dl_type);
         miniflow_push_be16(mf, vlan_tci, vlan_tci);
+        miniflow_push_be16(mf, vlan_tpid, vlan_tpid);
+        miniflow_push_be16(mf, vlan_count, vlan_count);
     }

     /* Parse mpls. */
@@ -656,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 == 26);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 27);

     fmd->dp_hash = flow->dp_hash;
     fmd->recirc_id = flow->recirc_id;
@@ -1316,7 +1349,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 == 26);
+        BUILD_ASSERT(FLOW_WC_SEQ == 27);
         memset((char *) flow + FLOW_SEGMENT_2_ENDS_AT, 0,
                sizeof(struct flow) - FLOW_SEGMENT_2_ENDS_AT);
     }
@@ -1487,7 +1520,7 @@ flow_compose(struct ofpbuf *b, const struct flow *flow)
     }

     if (flow->vlan_tci & htons(VLAN_CFI)) {
-        eth_push_vlan(b, htons(ETH_TYPE_VLAN), flow->vlan_tci);
+        eth_push_vlan(b, get_vlan_tpid(flow->vlan_count), flow->vlan_tci);
     }

     if (flow->dl_type == htons(ETH_TYPE_IP)) {
diff --git a/lib/flow.h b/lib/flow.h
index 139e7f6..07314dc 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 26
+#define FLOW_WC_SEQ 27

 #define FLOW_N_REGS 8
 BUILD_ASSERT_DECL(FLOW_N_REGS <= NXM_NX_MAX_REGS);
@@ -98,6 +98,8 @@ struct flow {
     uint8_t dl_src[6];          /* 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_tpid;         /* VLAN identifier; Either 802.1Q or 802.1AD */
+    uint8_t  vlan_count;        /* Number of stacked VLANs */
     ovs_be32 mpls_lse[FLOW_MAX_MPLS_LABELS]; /* MPLS label stack entry. */

     /* L3 */
@@ -129,8 +131,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) + 172
-                  && FLOW_WC_SEQ == 26);
+                  == sizeof(struct flow_tnl) + 176
+                  && FLOW_WC_SEQ == 27);

 /* Incremental points at which flow classification may be performed in
  * segments.
diff --git a/lib/match.c b/lib/match.c
index 93b725a..ff84d61 100644
--- a/lib/match.c
+++ b/lib/match.c
@@ -944,7 +944,7 @@ match_format(const struct match *match, struct ds
*s, unsigned int priority)

     int i;

-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 26);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 27);

     if (priority != OFP_DEFAULT_PRIORITY) {
         ds_put_format(s, "priority=%u,", priority);
diff --git a/lib/nx-match.c b/lib/nx-match.c
index 295eec0..678e6f3 100644
--- a/lib/nx-match.c
+++ b/lib/nx-match.c
@@ -616,7 +616,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 == 26);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 27);

     /* Metadata. */
     if (match->wc.masks.dp_hash) {
diff --git a/lib/odp-util.c b/lib/odp-util.c
index 3c69ada..e9f4dd8 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -3252,10 +3252,13 @@ parse_8021q_onward(const struct nlattr
*attrs[OVS_KEY_ATTR_MAX + 1],

     /* Set vlan_tci.
      * Remove the TPID from dl_type since it's not the real Ethertype.  */
+    flow->vlan_tpid = flow->dl_type;
     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));
+    flow->vlan_count = (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_VLAN)
+                        ? 1: 0);
     if (!is_mask) {
         if (!(present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_VLAN))) {
             return ODP_FIT_TOO_LITTLE;
@@ -3378,7 +3381,7 @@ 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)) {
+        : (eth_type_vlan(src_flow->dl_type))) {
         return parse_8021q_onward(attrs, present_attrs, out_of_range_attr,
                                   expected_attrs, flow, key, key_len,
src_flow);
     }
@@ -3568,24 +3571,42 @@ pop_vlan(struct flow *base,
     }
 }

+/* This function determines and commits the VLAN action.
+ *
+ * POP VLAN:
+ *         - Number of stacked VLANs is more in base flow than current flow.
+ *         - Same number of stacked VLANs but difference in VLAN tci.
+ *
+ * PUSH VLAN: Number of stacked VLANs in current flow greater than
+ *            or equal to base flow.
+ *
+ * Neither PUSH nor POP: Base and current flow has same vlan tci and count
+ */
 static void
-commit_vlan_action(ovs_be16 vlan_tci, struct flow *base,
+commit_vlan_action(const struct flow *flow, struct flow *base,
                    struct ofpbuf *odp_actions, struct flow_wildcards *wc)
 {
-    if (base->vlan_tci == vlan_tci) {
+    if ((base->vlan_tci == flow->vlan_tci) &&
+        (base->vlan_count == flow->vlan_count)) {
         return;
     }

-    pop_vlan(base, odp_actions, wc);
-    if (vlan_tci & htons(VLAN_CFI)) {
+    if (base->vlan_count > flow->vlan_count ||
+        (base->vlan_tci != flow->vlan_tci && base->vlan_count ==
flow->vlan_count))
+        pop_vlan(base, odp_actions, wc);
+
+    if (flow->vlan_count >= base->vlan_count &&
+        flow->vlan_tci & htons(VLAN_CFI)) {
         struct ovs_action_push_vlan vlan;

-        vlan.vlan_tpid = htons(ETH_TYPE_VLAN);
-        vlan.vlan_tci = vlan_tci;
+        vlan.vlan_tpid = get_vlan_tpid(flow->vlan_count);
+        vlan.vlan_tci  = flow->vlan_tci;
         nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_PUSH_VLAN,
                           &vlan, sizeof vlan);
     }
-    base->vlan_tci = vlan_tci;
+    base->vlan_tci = flow->vlan_tci;
+    base->vlan_tpid = flow->vlan_tpid;
+    base->vlan_count = flow->vlan_count;
 }

 static void
@@ -3885,7 +3906,7 @@ commit_odp_actions(const struct flow *flow,
struct flow *base,
     slow = commit_set_nw_action(flow, base, odp_actions, wc);
     commit_set_port_action(flow, base, odp_actions, wc);
     commit_mpls_action(flow, base, odp_actions, wc);
-    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);
     commit_set_pkt_mark_action(flow, base, odp_actions, wc);

diff --git a/lib/ofp-util.c b/lib/ofp-util.c
index 09e4438..9037fd4 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -132,7 +132,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 == 26);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 27);

     /* Initialize most of wc. */
     flow_wildcards_init_catchall(wc);
diff --git a/lib/packets.c b/lib/packets.c
index 6244c3f..ef1aebe 100644
--- a/lib/packets.c
+++ b/lib/packets.c
@@ -199,7 +199,7 @@ eth_pop_vlan(struct ofpbuf *packet)
     struct vlan_eth_header *veh = ofpbuf_l2(packet);

     if (veh && ofpbuf_size(packet) >= sizeof *veh
-        && veh->veth_type == htons(ETH_TYPE_VLAN)) {
+        && (eth_type_vlan(veh->veth_type))) {

         memmove((char *)veh + VLAN_HEADER_LEN, veh, 2 * ETH_ADDR_LEN);
         ofpbuf_resize_l2(packet, -VLAN_HEADER_LEN);
diff --git a/lib/packets.h b/lib/packets.h
index 4575dd0..0d175f4 100644
--- a/lib/packets.h
+++ b/lib/packets.h
@@ -249,6 +249,22 @@ 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) ||
+        eth_type == htons(ETH_TYPE_VLAN_8021AD);
+}
+
+/*
+ * Returns vlan tpid in network byte order considering vlan count
+ */
+static inline ovs_be16 get_vlan_tpid(uint8_t vlan_count)
+{
+    return (vlan_count >= 2)
+           ? htons(ETH_TYPE_VLAN_8021AD)
+           : htons(ETH_TYPE_VLAN);
+}
+
 /* Minimum value for an Ethernet type.  Values below this are IEEE 802.2 frame
  * lengths. */
 #define ETH_TYPE_MIN           0x600
--
1.9.0



More information about the dev mailing list