bridge: Use rcu_barrier() instead of syncronize_net() on unload.
[safe/jmp/linux-2.6] / net / bridge / br_forward.c
index ef9f209..d2c27c8 100644 (file)
@@ -5,8 +5,6 @@
  *     Authors:
  *     Lennert Buytenhek               <buytenh@gnu.org>
  *
- *     $Id: br_forward.c,v 1.4 2001/08/14 22:05:57 davem Exp $
- *
  *     This program is free software; you can redistribute it and/or
  *     modify it under the terms of the GNU General Public License
  *     as published by the Free Software Foundation; either version
 #include <linux/kernel.h>
 #include <linux/netdevice.h>
 #include <linux/skbuff.h>
+#include <linux/if_vlan.h>
 #include <linux/netfilter_bridge.h>
 #include "br_private.h"
 
-static inline int should_deliver(const struct net_bridge_port *p, 
+/* Don't forward packets to originating port or forwarding diasabled */
+static inline int should_deliver(const struct net_bridge_port *p,
                                 const struct sk_buff *skb)
 {
-       if (skb->dev == p->dev ||
-           p->state != BR_STATE_FORWARDING)
-               return 0;
+       return (skb->dev != p->dev && p->state == BR_STATE_FORWARDING);
+}
 
-       return 1;
+static inline unsigned packet_length(const struct sk_buff *skb)
+{
+       return skb->len - (skb->protocol == htons(ETH_P_8021Q) ? VLAN_HLEN : 0);
 }
 
 int br_dev_queue_push_xmit(struct sk_buff *skb)
 {
-       if (skb->len > skb->dev->mtu) 
+       /* drop mtu oversized packets except gso */
+       if (packet_length(skb) > skb->dev->mtu && !skb_is_gso(skb))
                kfree_skb(skb);
        else {
-#ifdef CONFIG_BRIDGE_NETFILTER
                /* ip_refrag calls ip_fragment, doesn't copy the MAC header. */
-               nf_bridge_maybe_copy_header(skb);
-#endif
-               skb_push(skb, ETH_HLEN);
+               if (nf_bridge_maybe_copy_header(skb))
+                       kfree_skb(skb);
+               else {
+                       skb_push(skb, ETH_HLEN);
 
-               dev_queue_xmit(skb);
+                       dev_queue_xmit(skb);
+               }
        }
 
        return 0;
@@ -48,18 +51,14 @@ int br_dev_queue_push_xmit(struct sk_buff *skb)
 
 int br_forward_finish(struct sk_buff *skb)
 {
-       NF_HOOK(PF_BRIDGE, NF_BR_POST_ROUTING, skb, NULL, skb->dev,
-                       br_dev_queue_push_xmit);
+       return NF_HOOK(PF_BRIDGE, NF_BR_POST_ROUTING, skb, NULL, skb->dev,
+                      br_dev_queue_push_xmit);
 
-       return 0;
 }
 
 static void __br_deliver(const struct net_bridge_port *to, struct sk_buff *skb)
 {
        skb->dev = to->dev;
-#ifdef CONFIG_NETFILTER_DEBUG
-       skb->nf_debug = 0;
-#endif
        NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
                        br_forward_finish);
 }
@@ -68,9 +67,14 @@ static void __br_forward(const struct net_bridge_port *to, struct sk_buff *skb)
 {
        struct net_device *indev;
 
+       if (skb_warn_if_lro(skb)) {
+               kfree_skb(skb);
+               return;
+       }
+
        indev = skb->dev;
        skb->dev = to->dev;
-       skb->ip_summed = CHECKSUM_NONE;
+       skb_forward_csum(skb);
 
        NF_HOOK(PF_BRIDGE, NF_BR_FORWARD, skb, indev, skb->dev,
                        br_forward_finish);
@@ -99,24 +103,13 @@ void br_forward(const struct net_bridge_port *to, struct sk_buff *skb)
 }
 
 /* called under bridge lock */
-static void br_flood(struct net_bridge *br, struct sk_buff *skb, int clone,
-       void (*__packet_hook)(const struct net_bridge_port *p, 
+static void br_flood(struct net_bridge *br, struct sk_buff *skb,
+       void (*__packet_hook)(const struct net_bridge_port *p,
                              struct sk_buff *skb))
 {
        struct net_bridge_port *p;
        struct net_bridge_port *prev;
 
-       if (clone) {
-               struct sk_buff *skb2;
-
-               if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL) {
-                       br->statistics.tx_dropped++;
-                       return;
-               }
-
-               skb = skb2;
-       }
-
        prev = NULL;
 
        list_for_each_entry_rcu(p, &br->port_list, list) {
@@ -125,7 +118,7 @@ static void br_flood(struct net_bridge *br, struct sk_buff *skb, int clone,
                                struct sk_buff *skb2;
 
                                if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL) {
-                                       br->statistics.tx_dropped++;
+                                       br->dev->stats.tx_dropped++;
                                        kfree_skb(skb);
                                        return;
                                }
@@ -147,13 +140,13 @@ static void br_flood(struct net_bridge *br, struct sk_buff *skb, int clone,
 
 
 /* called with rcu_read_lock */
-void br_flood_deliver(struct net_bridge *br, struct sk_buff *skb, int clone)
+void br_flood_deliver(struct net_bridge *br, struct sk_buff *skb)
 {
-       br_flood(br, skb, clone, __br_deliver);
+       br_flood(br, skb, __br_deliver);
 }
 
 /* called under bridge lock */
-void br_flood_forward(struct net_bridge *br, struct sk_buff *skb, int clone)
+void br_flood_forward(struct net_bridge *br, struct sk_buff *skb)
 {
-       br_flood(br, skb, clone, __br_forward);
+       br_flood(br, skb, __br_forward);
 }