]> Pileus Git - ~andy/linux/blobdiff - net/bridge/br_vlan.c
bridge: Fix handling stacked vlan tags
[~andy/linux] / net / bridge / br_vlan.c
index 8249ca764c79c5f2ddab51006ad445752b3ac137..c77eed56b045868b3d1efc5a6a339a09ad66a43b 100644 (file)
@@ -119,22 +119,6 @@ static void __vlan_flush(struct net_port_vlans *v)
        kfree_rcu(v, rcu);
 }
 
-/* Strip the tag from the packet.  Will return skb with tci set 0.  */
-static struct sk_buff *br_vlan_untag(struct sk_buff *skb)
-{
-       if (skb->protocol != htons(ETH_P_8021Q)) {
-               skb->vlan_tci = 0;
-               return skb;
-       }
-
-       skb->vlan_tci = 0;
-       skb = vlan_untag(skb);
-       if (skb)
-               skb->vlan_tci = 0;
-
-       return skb;
-}
-
 struct sk_buff *br_handle_vlan(struct net_bridge *br,
                               const struct net_port_vlans *pv,
                               struct sk_buff *skb)
@@ -150,7 +134,7 @@ struct sk_buff *br_handle_vlan(struct net_bridge *br,
         */
        br_vlan_get_tag(skb, &vid);
        if (test_bit(vid, pv->untagged_bitmap))
-               skb = br_vlan_untag(skb);
+               skb->vlan_tci = 0;
 
 out:
        return skb;
@@ -174,6 +158,18 @@ bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v,
        if (!v)
                return false;
 
+       /* If vlan tx offload is disabled on bridge device and frame was
+        * sent from vlan device on the bridge device, it does not have
+        * HW accelerated vlan tag.
+        */
+       if (unlikely(!vlan_tx_tag_present(skb) &&
+                    (skb->protocol == htons(ETH_P_8021Q) ||
+                     skb->protocol == htons(ETH_P_8021AD)))) {
+               skb = vlan_untag(skb);
+               if (unlikely(!skb))
+                       return false;
+       }
+
        err = br_vlan_get_tag(skb, vid);
        if (!*vid) {
                u16 pvid = br_get_pvid(v);