[ovs-dev] [PATCH 1/4] datapath: Kernel support for provider VLANs

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


Kernel recognizes and accepts ethertype of provider VLANs (ETH_P_8021AD)
along with normal VLAN (ETH_P_8021Q). Also in flow key formation,
only the outermost VLAN is considered. Remaining stacked VLANs are skipped.

Signed-off-by: Avinash <avinashandrew at gmail.com>
---
 datapath/actions.c      | 10 ++++++----
 datapath/flow.c         | 34 ++++++++++++++++++++++++++++++++--
 datapath/flow_netlink.c | 17 +++++++++++++----
 3 files changed, 51 insertions(+), 10 deletions(-)

diff --git a/datapath/actions.c b/datapath/actions.c
index 603c7cb..8dd11a0 100644
--- a/datapath/actions.c
+++ b/datapath/actions.c
@@ -84,8 +84,9 @@ static int pop_vlan(struct sk_buff *skb)
        if (likely(vlan_tx_tag_present(skb))) {
                vlan_set_tci(skb, 0);
        } else {
-               if (unlikely(skb->protocol != htons(ETH_P_8021Q) ||
-                            skb->len < VLAN_ETH_HLEN))
+                if (unlikely((skb->protocol != htons(ETH_P_8021Q)
+                              && skb->protocol != htons(ETH_P_8021AD))
+                            || skb->len < VLAN_ETH_HLEN))
                        return 0;

                err = __pop_vlan_tci(skb, &tci);
@@ -93,8 +94,9 @@ static int pop_vlan(struct sk_buff *skb)
                        return err;
        }
        /* move next vlan tag to hw accel tag */
-       if (likely(skb->protocol != htons(ETH_P_8021Q) ||
-                  skb->len < VLAN_ETH_HLEN))
+        if (likely((skb->protocol != htons(ETH_P_8021Q)
+                    && skb->protocol != htons(ETH_P_8021AD))
+                  || skb->len < VLAN_ETH_HLEN))
                return 0;

        err = __pop_vlan_tci(skb, &tci);
diff --git a/datapath/flow.c b/datapath/flow.c
index c52081b..c64dfe4 100644
--- a/datapath/flow.c
+++ b/datapath/flow.c
@@ -290,7 +290,7 @@ static bool icmp6hdr_ok(struct sk_buff *skb)
 static int parse_vlan(struct sk_buff *skb, struct sw_flow_key *key)
 {
        struct qtag_prefix {
-               __be16 eth_type; /* ETH_P_8021Q */
+               __be16 eth_type; /* ETH_P_8021Q or ETH_P_8021AD */
                __be16 tci;
        };
        struct qtag_prefix *qp;
@@ -309,6 +309,32 @@ static int parse_vlan(struct sk_buff *skb, struct
sw_flow_key *key)
        return 0;
 }

+static int parse_remaining_vlans(struct sk_buff *skb)
+{
+    struct qtag_prefix {
+        __be16 eth_type;
+        __be16 tci;
+    };
+
+    struct qtag_prefix *qp;
+    if (unlikely(skb->len < sizeof(struct qtag_prefix) + sizeof(__be16)))
+        return 0;
+
+    if (unlikely(!pskb_may_pull(skb, sizeof(struct qtag_prefix) +
+                                sizeof(__be16))))
+        return -ENOMEM;
+
+    qp = (struct qtag_prefix *) skb->data;
+    while ((qp->eth_type == htons(ETH_P_8021Q) ||
+            qp->eth_type == htons(ETH_P_8021AD)))
+    {
+        __skb_pull(skb, sizeof(struct qtag_prefix));
+        qp = (struct qtag_prefix *) skb->data;
+    }
+
+    return 0;
+}
+
 static __be16 parse_ethertype(struct sk_buff *skb)
 {
        struct llc_snap_hdr {
@@ -471,10 +497,14 @@ int ovs_flow_extract(struct sk_buff *skb, u16
in_port, struct sw_flow_key *key)

        if (vlan_tx_tag_present(skb))
                key->eth.tci = htons(vlan_get_tci(skb));
-       else if (eth->h_proto == htons(ETH_P_8021Q))
+        else if (eth->h_proto == htons(ETH_P_8021Q) ||
+                 eth->h_proto == htons(ETH_P_8021AD))
                if (unlikely(parse_vlan(skb, key)))
                        return -ENOMEM;

+        if (unlikely(parse_remaining_vlans(skb)))
+                return -ENOMEM;
+
        key->eth.type = parse_ethertype(skb);
        if (unlikely(key->eth.type == htons(0)))
                return -ENOMEM;
diff --git a/datapath/flow_netlink.c b/datapath/flow_netlink.c
index 803a94c..54b13fc 100644
--- a/datapath/flow_netlink.c
+++ b/datapath/flow_netlink.c
@@ -764,7 +764,8 @@ int ovs_nla_get_match(struct sw_flow_match *match,

        if ((key_attrs & (1ULL << OVS_KEY_ATTR_ETHERNET)) &&
            (key_attrs & (1ULL << OVS_KEY_ATTR_ETHERTYPE)) &&
-           (nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]) == htons(ETH_P_8021Q))) {
+            ((nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]) == htons(ETH_P_8021Q))
+              || (nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]) ==
htons(ETH_P_8021AD)))) {
                __be16 tci;

                if (!((key_attrs & (1ULL << OVS_KEY_ATTR_VLAN)) &&
@@ -937,9 +938,16 @@ int ovs_nla_put_flow(const struct sw_flow_key *swkey,
        ether_addr_copy(eth_key->eth_src, output->eth.src);
        ether_addr_copy(eth_key->eth_dst, output->eth.dst);

-       if (swkey->eth.tci || swkey->eth.type == htons(ETH_P_8021Q)) {
+        if (swkey->eth.tci || swkey->eth.type == htons(ETH_P_8021Q)
+            || swkey->eth.type == htons(ETH_P_8021AD)) {
                __be16 eth_type;
-               eth_type = !is_mask ? htons(ETH_P_8021Q) : htons(0xffff);
+                if (is_mask)
+                    eth_type = htons(0xffff);
+                else
+                    eth_type = (swkey->eth.type == htons(ETH_P_8021AD))
+                               ? htons(ETH_P_8021AD)
+                               : htons(ETH_P_8021Q);
+
                if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE, eth_type) ||
                    nla_put_be16(skb, OVS_KEY_ATTR_VLAN, output->eth.tci))
                        goto nla_put_failure;
@@ -1493,7 +1501,8 @@ int ovs_nla_copy_actions(const struct nlattr *attr,

                case OVS_ACTION_ATTR_PUSH_VLAN:
                        vlan = nla_data(a);
-                       if (vlan->vlan_tpid != htons(ETH_P_8021Q))
+                        if (vlan->vlan_tpid != htons(ETH_P_8021Q)
+                            && vlan->vlan_tpid != htons(ETH_P_8021AD))
                                return -EINVAL;
                        if (!(vlan->vlan_tci & htons(VLAN_TAG_PRESENT)))
                                return -EINVAL;
--
1.9.0



More information about the dev mailing list