bridge: Fix inabillity to retrieve vlan tags when tx offload is disabled
authorToshiaki Makita <makita.toshiaki@lab.ntt.co.jp>
Thu, 27 Mar 2014 12:46:55 +0000 (21:46 +0900)
committerDavid S. Miller <davem@davemloft.net>
Fri, 28 Mar 2014 20:33:09 +0000 (16:33 -0400)
Bridge vlan code (br_vlan_get_tag()) assumes that all frames have vlan_tci
if they are tagged, but if vlan tx offload is manually disabled on bridge
device and frames are sent from vlan device on the bridge device, the tags
are embedded in skb->data and they break this assumption.
Extract embedded vlan tags and move them to vlan_tci at ingress.

Signed-off-by: Toshiaki Makita <makita.toshiaki@lab.ntt.co.jp>
Acked-by: Vlad Yasevich <vyasevic@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/bridge/br_device.c
net/bridge/br_vlan.c

index 63f0455c0bc3e21fea311a4d14d24a995c606d6f..8fe8b71b487add263a711d39e52f683675708dd6 100644 (file)
@@ -49,14 +49,14 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
        brstats->tx_bytes += skb->len;
        u64_stats_update_end(&brstats->syncp);
 
-       if (!br_allowed_ingress(br, br_get_vlan_info(br), skb, &vid))
-               goto out;
-
        BR_INPUT_SKB_CB(skb)->brdev = dev;
 
        skb_reset_mac_header(skb);
        skb_pull(skb, ETH_HLEN);
 
+       if (!br_allowed_ingress(br, br_get_vlan_info(br), skb, &vid))
+               goto out;
+
        if (is_broadcast_ether_addr(dest))
                br_flood_deliver(br, skb, false);
        else if (is_multicast_ether_addr(dest)) {
index 8249ca764c79c5f2ddab51006ad445752b3ac137..44f31af0b965d6365d748d45951f9ad8bb9714c7 100644 (file)
@@ -174,6 +174,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);