This is the linux kernel portion of the patch.

Signed-off-by: Thomas F Herbert <thomasfherb...@entpnt.com>
---
 datapath/actions.c                                | 27 ++++++--
 datapath/flow.c                                   | 80 +++++++++++++++++++----
 datapath/flow.h                                   |  1 +
 datapath/flow_netlink.c                           | 37 +++++++++--
 datapath/linux/compat/include/linux/openvswitch.h | 17 ++---
 5 files changed, 130 insertions(+), 32 deletions(-)

diff --git a/datapath/actions.c b/datapath/actions.c
index b527cb6..f6b3b4e 100644
--- a/datapath/actions.c
+++ b/datapath/actions.c
@@ -255,17 +255,18 @@ static int __pop_vlan_tci(struct sk_buff *skb, __be16 
*current_tci)
 
        return 0;
 }
-
 static int pop_vlan(struct sk_buff *skb, struct sw_flow_key *key)
 {
        __be16 tci;
        int err;
+       __be16 prot;
 
        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);
@@ -284,13 +285,20 @@ static int pop_vlan(struct sk_buff *skb, struct 
sw_flow_key *key)
        if (unlikely(err))
                return err;
 
-       __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), ntohs(tci));
+       if (unlikely(skb->protocol == htons(ETH_P_8021AD)))
+               prot = htons(ETH_P_8021AD);
+        else
+               prot = htons(ETH_P_8021Q);
+
+       __vlan_hwaccel_put_tag(skb, prot, ntohs(tci));
+
        return 0;
 }
 
 static int push_vlan(struct sk_buff *skb, struct sw_flow_key *key,
                     const struct ovs_action_push_vlan *vlan)
 {
+
        if (unlikely(vlan_tx_tag_present(skb))) {
                u16 current_tag;
 
@@ -303,15 +311,20 @@ static int push_vlan(struct sk_buff *skb, struct 
sw_flow_key *key,
                /* Update mac_len for subsequent MPLS actions */
                skb->mac_len += VLAN_HLEN;
 
-               if (skb->ip_summed == CHECKSUM_COMPLETE)
-                       skb->csum = csum_add(skb->csum, csum_partial(skb->data
+               skb->csum = csum_add(skb->csum, csum_partial(skb->data
                                        + (2 * ETH_ALEN), VLAN_HLEN, 0));
 
                invalidate_flow_key(key);
        } else {
                key->eth.tci = vlan->vlan_tci;
        }
-       __vlan_hwaccel_put_tag(skb, vlan->vlan_tpid, ntohs(vlan->vlan_tci) & 
~VLAN_TAG_PRESENT);
+
+        if (likely(vlan->vlan_tpid == htons(ETH_P_8021Q))) {
+               __vlan_hwaccel_put_tag(skb, vlan->vlan_tpid, 
ntohs(vlan->vlan_tci) & ~VLAN_TAG_PRESENT);
+        } else {
+               __vlan_put_tag(skb, vlan->vlan_tpid, ntohs(vlan->vlan_tci) & 
~VLAN_TAG_PRESENT);
+               skb->vlan_tci &= ~VLAN_TAG_PRESENT;
+       }
        return 0;
 }
 
diff --git a/datapath/flow.c b/datapath/flow.c
index 62ce331..30f791d 100644
--- a/datapath/flow.c
+++ b/datapath/flow.c
@@ -295,21 +295,75 @@ 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;
+       struct qtag_prefix *qp = (struct qtag_prefix *) skb->data;
 
-       if (unlikely(skb->len < sizeof(struct qtag_prefix) + sizeof(__be16)))
-               return 0;
+       struct qinqtag_prefix {
+               __be16 eth_type; /* ETH_P_8021Q  or ETH_P_8021AD */
+               __be16 tci;
+               __be16 encapsulated_proto; /* ETH_P_8021Q */
+               __be16 ctci;
+       };
 
-       if (unlikely(!pskb_may_pull(skb, sizeof(struct qtag_prefix) +
-                                        sizeof(__be16))))
-               return -ENOMEM;
+       if (likely(skb->vlan_tci | htons(VLAN_TAG_PRESENT))) {
+
+               key->eth.tci = skb->vlan_tci | htons(VLAN_TAG_PRESENT);
+
+               if(unlikely(skb->vlan_proto == htons(ETH_P_8021AD))) {
+                       struct qtag_prefix *qp = (struct qtag_prefix *) 
skb->data;
+
+                       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;
+                       }
+
+                       if (likely(qp->eth_type == htons(ETH_P_8021Q))) {
+                               key->eth.ctci = qp->tci | 
htons(VLAN_TAG_PRESENT);
+                               __skb_pull(skb, sizeof(struct qtag_prefix));
+                       }
+               }
+                return 0;
+       }
+
+
+        if (qp->eth_type == htons(ETH_P_8021AD)) {
+               struct qinqtag_prefix *qinqp = (struct qinqtag_prefix *) 
skb->data;
 
-       qp = (struct qtag_prefix *) skb->data;
-       key->eth.tci = qp->tci | htons(VLAN_TAG_PRESENT);
-       __skb_pull(skb, sizeof(struct qtag_prefix));
+               if (unlikely(skb->len < sizeof(struct qinqtag_prefix) +
+                                       sizeof(__be16)))
+                       return 0;
+
+               if (unlikely(!pskb_may_pull(skb, sizeof(struct qinqtag_prefix) +
+                               sizeof(__be16)))) {
+                       return -ENOMEM;
+               }
+               key->eth.tci = qinqp->tci | htons(VLAN_TAG_PRESENT);
+               key->eth.ctci = qinqp->ctci | htons(VLAN_TAG_PRESENT);
+
+               __skb_pull(skb, sizeof(struct qinqtag_prefix));
+
+                return 0;
+
+       }
+        if (qp->eth_type == htons(ETH_P_8021Q)) {
+
+               if (unlikely(skb->len < sizeof(struct qtag_prefix) +
+                                       sizeof(__be16)))
+                       return -ENOMEM;
+
+               if (unlikely(!pskb_may_pull(skb, sizeof(struct qtag_prefix) +
+                               sizeof(__be16))))
+                       return 0;
+               key->eth.tci = qp->tci | htons(VLAN_TAG_PRESENT);
+
+               __skb_pull(skb, sizeof(struct qtag_prefix));
+       }
 
        return 0;
 }
@@ -470,9 +524,9 @@ static int key_extract(struct sk_buff *skb, struct 
sw_flow_key *key)
         * update skb->csum here. */
 
        key->eth.tci = 0;
-       if (vlan_tx_tag_present(skb))
-               key->eth.tci = htons(vlan_get_tci(skb));
-       else if (eth->h_proto == htons(ETH_P_8021Q))
+       if ((vlan_tx_tag_present(skb)) ||
+               (eth->h_proto == htons(ETH_P_8021Q)) ||
+               (eth->h_proto == htons(ETH_P_8021AD)))
                if (unlikely(parse_vlan(skb, key)))
                        return -ENOMEM;
 
diff --git a/datapath/flow.h b/datapath/flow.h
index c78b864..937ba6d 100644
--- a/datapath/flow.h
+++ b/datapath/flow.h
@@ -137,6 +137,7 @@ struct sw_flow_key {
                u8     src[ETH_ALEN];   /* Ethernet source address. */
                u8     dst[ETH_ALEN];   /* Ethernet destination address. */
                __be16 tci;             /* 0 if no VLAN, VLAN_TAG_PRESENT set 
otherwise. */
+               __be16 ctci;            /* 0 if no VLAN, VLAN_TAG_PRESENT set 
otherwise. */
                __be16 type;            /* Ethernet frame type. */
        } eth;
        union {
diff --git a/datapath/flow_netlink.c b/datapath/flow_netlink.c
index 762ea03..b1aeebd 100644
--- a/datapath/flow_netlink.c
+++ b/datapath/flow_netlink.c
@@ -271,7 +271,7 @@ size_t ovs_key_attr_size(void)
 {
        /* Whenever adding new OVS_KEY_ FIELDS, we should consider
         * updating this function.  */
-       BUILD_BUG_ON(OVS_KEY_ATTR_TUNNEL_INFO != 22);
+       BUILD_BUG_ON(OVS_KEY_ATTR_TUNNEL_INFO != 23);
 
        return    nla_total_size(4)   /* OVS_KEY_ATTR_PRIORITY */
                + nla_total_size(0)   /* OVS_KEY_ATTR_TUNNEL */
@@ -725,6 +725,24 @@ static int ovs_key_from_nlattrs(struct sw_flow_match 
*match, u64 attrs,
                attrs &= ~(1ULL << OVS_KEY_ATTR_VLAN);
        }
 
+       if (attrs & (1ULL << OVS_KEY_ATTR_CVLAN)) {
+               __be16 ctci;
+
+               ctci = nla_get_be16(a[OVS_KEY_ATTR_CVLAN]);
+               if (!(ctci & htons(VLAN_TAG_PRESENT))) {
+                       if (is_mask)
+                               OVS_NLERR(log,"VLAN customer TCI mask does not 
have exact match for VLAN_TAG_PRESENT bit.\n");
+                       else
+                               OVS_NLERR(log,"VLAN customer TCI does not have 
VLAN_TAG_PRESENT bit set.\n");
+
+                       return -EINVAL;
+               }
+
+               SW_FLOW_KEY_PUT(match, eth.ctci, ctci, is_mask);
+               attrs &= ~(1ULL << OVS_KEY_ATTR_CVLAN);
+       } //else if (!is_mask)
+       //      SW_FLOW_KEY_PUT(match, eth.ctci, htons(0xffff), true);
+
        if (attrs & (1ULL << OVS_KEY_ATTR_ETHERTYPE)) {
                __be16 eth_type;
 
@@ -977,8 +995,9 @@ int ovs_nla_get_match(struct sw_flow_match *match,
                return err;
 
        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))) {
+               (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_8021AD)))) {
                __be16 tci;
 
                if (!((key_attrs & (1ULL << OVS_KEY_ATTR_VLAN)) &&
@@ -1204,6 +1223,15 @@ int ovs_nla_put_flow(const struct sw_flow_key *swkey,
                encap = nla_nest_start(skb, OVS_KEY_ATTR_ENCAP);
                if (!swkey->eth.tci)
                        goto unencap;
+       } else if (swkey->eth.tci || swkey->eth.type == htons(ETH_P_8021AD)) {
+               __be16 eth_type;
+               eth_type = !is_mask ? htons(ETH_P_8021AD) : htons(0xffff);
+               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;
+               encap = nla_nest_start(skb, OVS_KEY_ATTR_ENCAP);
+               if (!swkey->eth.tci)
+                       goto unencap;
        } else
                encap = NULL;
 
@@ -1837,7 +1865,8 @@ static 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;
diff --git a/datapath/linux/compat/include/linux/openvswitch.h 
b/datapath/linux/compat/include/linux/openvswitch.h
index b2257e6..f6abf28 100644
--- a/datapath/linux/compat/include/linux/openvswitch.h
+++ b/datapath/linux/compat/include/linux/openvswitch.h
@@ -310,6 +310,7 @@ enum ovs_key_attr {
        OVS_KEY_ATTR_IN_PORT,   /* u32 OVS dp port number */
        OVS_KEY_ATTR_ETHERNET,  /* struct ovs_key_ethernet */
        OVS_KEY_ATTR_VLAN,      /* be16 VLAN TCI */
+       OVS_KEY_ATTR_CVLAN,     /* be16 VLAN Customer inner TCI */
        OVS_KEY_ATTR_ETHERTYPE, /* be16 Ethernet type */
        OVS_KEY_ATTR_IPV4,      /* struct ovs_key_ipv4 */
        OVS_KEY_ATTR_IPV6,      /* struct ovs_key_ipv6 */
@@ -550,14 +551,14 @@ struct ovs_action_push_mpls {
  * @vlan_tci: Tag control identifier (TCI) to push.  The CFI bit must be set
  * (but it will not be set in the 802.1Q header that is pushed).
  *
- * The @vlan_tpid value is typically %ETH_P_8021Q.  The only acceptable TPID
- * values are those that the kernel module also parses as 802.1Q headers, to
- * prevent %OVS_ACTION_ATTR_PUSH_VLAN followed by %OVS_ACTION_ATTR_POP_VLAN
- * from having surprising results.
+ * The @vlan_tpid value is typically %ETH_P_8021Q or %ETH_P_8021AD.
+ * The only acceptable TPID values are those that the kernel module also parses
+ * as 802.1Q headers, to prevent %OVS_ACTION_ATTR_PUSH_VLAN followed by
+ * %OVS_ACTION_ATTR_POP_VLAN from having surprising results.
  */
 struct ovs_action_push_vlan {
-       __be16 vlan_tpid;       /* 802.1Q TPID. */
-       __be16 vlan_tci;        /* 802.1Q TCI (VLAN ID and priority). */
+       __be16 vlan_tpid;       /* 802.1Q or 802.1ad TPID. */
+       __be16 vlan_tci;        /* 802.1Q or 802.1ad TCI (VLAN ID and 
priority). */
 };
 
 /* Data path hash algorithm for computing Datapath hash.
@@ -586,9 +587,9 @@ struct ovs_action_hash {
  * @OVS_ACTION_ATTR_OUTPUT: Output packet to port.
  * @OVS_ACTION_ATTR_USERSPACE: Send packet to userspace according to nested
  * %OVS_USERSPACE_ATTR_* attributes.
- * @OVS_ACTION_ATTR_PUSH_VLAN: Push a new outermost 802.1Q header onto the
+ * @OVS_ACTION_ATTR_PUSH_VLAN: Push a new outermost 802.1Q or 802.1ad header 
onto the
  * packet.
- * @OVS_ACTION_ATTR_POP_VLAN: Pop the outermost 802.1Q header off the packet.
+ * @OVS_ACTION_ATTR_POP_VLAN: Pop the outermost 802.1Q or 802.1ad header off 
the packet.
  * @OVS_ACTION_ATTR_SAMPLE: Probabilitically executes actions, as specified in
  * the nested %OVS_SAMPLE_ATTR_* attributes.
  * @OVS_ACTION_ATTR_SET: Replaces the contents of an existing header.  The
-- 
1.8.3.2

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

Reply via email to