bridge: Forward reserved group addresses if !STP
[pandora-kernel.git] / net / bridge / br_input.c
index d36e700..25207a1 100644 (file)
@@ -27,8 +27,10 @@ static int br_pass_frame_up(struct sk_buff *skb)
        struct net_bridge *br = netdev_priv(brdev);
        struct br_cpu_netstats *brstats = this_cpu_ptr(br->stats);
 
+       u64_stats_update_begin(&brstats->syncp);
        brstats->rx_packets++;
        brstats->rx_bytes += skb->len;
+       u64_stats_update_end(&brstats->syncp);
 
        indev = skb->dev;
        skb->dev = brdev;
@@ -37,11 +39,11 @@ static int br_pass_frame_up(struct sk_buff *skb)
                       netif_receive_skb);
 }
 
-/* note: already called with rcu_read_lock (preempt_disabled) */
+/* note: already called with rcu_read_lock */
 int br_handle_frame_finish(struct sk_buff *skb)
 {
        const unsigned char *dest = eth_hdr(skb)->h_dest;
-       struct net_bridge_port *p = rcu_dereference(skb->dev->br_port);
+       struct net_bridge_port *p = br_port_get_rcu(skb->dev);
        struct net_bridge *br;
        struct net_bridge_fdb_entry *dst;
        struct net_bridge_mdb_entry *mdst;
@@ -108,13 +110,12 @@ drop:
        goto out;
 }
 
-/* note: already called with rcu_read_lock (preempt_disabled) */
+/* note: already called with rcu_read_lock */
 static int br_handle_local_finish(struct sk_buff *skb)
 {
-       struct net_bridge_port *p = rcu_dereference(skb->dev->br_port);
+       struct net_bridge_port *p = br_port_get_rcu(skb->dev);
 
-       if (p)
-               br_fdb_update(p->br, p, eth_hdr(skb)->h_source);
+       br_fdb_update(p->br, p, eth_hdr(skb)->h_source);
        return 0;        /* process further */
 }
 
@@ -131,15 +132,18 @@ static inline int is_link_local(const unsigned char *dest)
 }
 
 /*
- * Called via br_handle_frame_hook.
  * Return NULL if skb is handled
- * note: already called with rcu_read_lock (preempt_disabled)
+ * note: already called with rcu_read_lock
  */
-struct sk_buff *br_handle_frame(struct net_bridge_port *p, struct sk_buff *skb)
+struct sk_buff *br_handle_frame(struct sk_buff *skb)
 {
+       struct net_bridge_port *p;
        const unsigned char *dest = eth_hdr(skb)->h_dest;
        int (*rhook)(struct sk_buff *skb);
 
+       if (unlikely(skb->pkt_type == PACKET_LOOPBACK))
+               return skb;
+
        if (!is_valid_ether_addr(eth_hdr(skb)->h_source))
                goto drop;
 
@@ -147,13 +151,15 @@ struct sk_buff *br_handle_frame(struct net_bridge_port *p, struct sk_buff *skb)
        if (!skb)
                return NULL;
 
+       p = br_port_get_rcu(skb->dev);
+
        if (unlikely(is_link_local(dest))) {
                /* Pause frames shouldn't be passed up by driver anyway */
                if (skb->protocol == htons(ETH_P_PAUSE))
                        goto drop;
 
                /* If STP is turned off, then forward */
-               if (p->br->stp_enabled == BR_NO_STP && dest[5] == 0)
+               if (p->br->stp_enabled == BR_NO_STP)
                        goto forward;
 
                if (NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev,