openvswitch: 802.1AD Flow handling, actions, vlan parsing, netlink attributes
[cascardo/linux.git] / net / openvswitch / flow.c
index 0ea128e..1240ae3 100644 (file)
@@ -302,24 +302,57 @@ static bool icmp6hdr_ok(struct sk_buff *skb)
                                  sizeof(struct icmp6hdr));
 }
 
-static int parse_vlan(struct sk_buff *skb, struct sw_flow_key *key)
+/**
+ * Parse vlan tag from vlan header.
+ * Returns ERROR on memory error.
+ * Returns 0 if it encounters a non-vlan or incomplete packet.
+ * Returns 1 after successfully parsing vlan tag.
+ */
+static int parse_vlan_tag(struct sk_buff *skb, struct vlan_head *key_vh)
 {
-       struct qtag_prefix {
-               __be16 eth_type; /* ETH_P_8021Q */
-               __be16 tci;
-       };
-       struct qtag_prefix *qp;
+       struct vlan_head *vh = (struct vlan_head *)skb->data;
 
-       if (unlikely(skb->len < sizeof(struct qtag_prefix) + sizeof(__be16)))
+       if (likely(!eth_type_vlan(vh->tpid)))
                return 0;
 
-       if (unlikely(!pskb_may_pull(skb, sizeof(struct qtag_prefix) +
-                                        sizeof(__be16))))
+       if (unlikely(skb->len < sizeof(struct vlan_head) + sizeof(__be16)))
+               return 0;
+
+       if (unlikely(!pskb_may_pull(skb, sizeof(struct vlan_head) +
+                                sizeof(__be16))))
                return -ENOMEM;
 
-       qp = (struct qtag_prefix *) skb->data;
-       key->eth.tci = qp->tci | htons(VLAN_TAG_PRESENT);
-       __skb_pull(skb, sizeof(struct qtag_prefix));
+       vh = (struct vlan_head *)skb->data;
+       key_vh->tci = vh->tci | htons(VLAN_TAG_PRESENT);
+       key_vh->tpid = vh->tpid;
+
+       __skb_pull(skb, sizeof(struct vlan_head));
+       return 1;
+}
+
+static int parse_vlan(struct sk_buff *skb, struct sw_flow_key *key)
+{
+       int res;
+
+       key->eth.vlan.tci = 0;
+       key->eth.vlan.tpid = 0;
+       key->eth.cvlan.tci = 0;
+       key->eth.cvlan.tpid = 0;
+
+       if (likely(skb_vlan_tag_present(skb))) {
+               key->eth.vlan.tci = htons(skb->vlan_tci);
+               key->eth.vlan.tpid = skb->vlan_proto;
+       } else {
+               /* Parse outer vlan tag in the non-accelerated case. */
+               res = parse_vlan_tag(skb, &key->eth.vlan);
+               if (res <= 0)
+                       return res;
+       }
+
+       /* Parse inner vlan tag. */
+       res = parse_vlan_tag(skb, &key->eth.cvlan);
+       if (res <= 0)
+               return res;
 
        return 0;
 }
@@ -480,12 +513,8 @@ static int key_extract(struct sk_buff *skb, struct sw_flow_key *key)
         * update skb->csum here.
         */
 
-       key->eth.tci = 0;
-       if (skb_vlan_tag_present(skb))
-               key->eth.tci = htons(skb->vlan_tci);
-       else if (eth->h_proto == htons(ETH_P_8021Q))
-               if (unlikely(parse_vlan(skb, key)))
-                       return -ENOMEM;
+       if (unlikely(parse_vlan(skb, key)))
+               return -ENOMEM;
 
        key->eth.type = parse_ethertype(skb);
        if (unlikely(key->eth.type == htons(0)))