]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
Add bridge patches from shemminger
authorChris Wright <chrisw@sous-sol.org>
Wed, 8 Feb 2006 00:51:49 +0000 (16:51 -0800)
committerChris Wright <chrisw@sous-sol.org>
Wed, 8 Feb 2006 00:51:49 +0000 (16:51 -0800)
queue/bridge-fix-rcu-race-on-device-removal.patch [new file with mode: 0644]
queue/bridge-netfilter-races-on-device-removal.patch [new file with mode: 0644]
queue/series

diff --git a/queue/bridge-fix-rcu-race-on-device-removal.patch b/queue/bridge-fix-rcu-race-on-device-removal.patch
new file mode 100644 (file)
index 0000000..a786da4
--- /dev/null
@@ -0,0 +1,111 @@
+From stable-bounces@linux.kernel.org  Mon Feb  6 15:50:01 2006
+Date: Mon, 6 Feb 2006 15:42:45 -0800
+From: Stephen Hemminger <shemminger@osdl.org>
+To: stable@kernel.org
+Cc: 
+Subject: [PATCH] bridge: fix RCU race on device removal
+
+Patch to 2.6.15 stable kernel to fix race conditions on device
+removal.  These are reproducible by doing delif while packets are
+in flight.
+
+Signed-off-by: Stephen Hemminger <shemminger@osdl.org>
+Signed-off-by: Chris Wright <chrisw@sous-sol.org>
+---
+
+ net/bridge/br_if.c       |    7 ++-----
+ net/bridge/br_input.c    |   10 ++++++++--
+ net/bridge/br_stp_bpdu.c |    8 ++++++--
+ 3 files changed, 16 insertions(+), 9 deletions(-)
+
+Index: linux-2.6.15.3/net/bridge/br_if.c
+===================================================================
+--- linux-2.6.15.3.orig/net/bridge/br_if.c
++++ linux-2.6.15.3/net/bridge/br_if.c
+@@ -99,7 +99,6 @@ static void del_nbp(struct net_bridge_po
+       struct net_bridge *br = p->br;
+       struct net_device *dev = p->dev;
+-      dev->br_port = NULL;
+       dev_set_promiscuity(dev, -1);
+       spin_lock_bh(&br->lock);
+@@ -110,9 +109,7 @@ static void del_nbp(struct net_bridge_po
+       list_del_rcu(&p->list);
+-      del_timer_sync(&p->message_age_timer);
+-      del_timer_sync(&p->forward_delay_timer);
+-      del_timer_sync(&p->hold_timer);
++      rcu_assign_pointer(dev->br_port, NULL);
+       
+       call_rcu(&p->rcu, destroy_nbp_rcu);
+ }
+@@ -217,7 +214,6 @@ static struct net_bridge_port *new_nbp(s
+       p->dev = dev;
+       p->path_cost = cost;
+       p->priority = 0x8000 >> BR_PORT_BITS;
+-      dev->br_port = p;
+       p->port_no = index;
+       br_init_port(p);
+       p->state = BR_STATE_DISABLED;
+@@ -360,6 +356,7 @@ int br_add_if(struct net_bridge *br, str
+       else if ((err = br_sysfs_addif(p)))
+               del_nbp(p);
+       else {
++              rcu_assign_pointer(dev->br_port, p);
+               dev_set_promiscuity(dev, 1);
+               list_add_rcu(&p->list, &br->port_list);
+Index: linux-2.6.15.3/net/bridge/br_input.c
+===================================================================
+--- linux-2.6.15.3.orig/net/bridge/br_input.c
++++ linux-2.6.15.3/net/bridge/br_input.c
+@@ -45,11 +45,17 @@ static void br_pass_frame_up(struct net_
+ int br_handle_frame_finish(struct sk_buff *skb)
+ {
+       const unsigned char *dest = eth_hdr(skb)->h_dest;
+-      struct net_bridge_port *p = skb->dev->br_port;
+-      struct net_bridge *br = p->br;
++      struct net_bridge_port *p = rcu_dereference(skb->dev->br_port);
++      struct net_bridge *br;
+       struct net_bridge_fdb_entry *dst;
+       int passedup = 0;
++      if (unlikely(!p || p->state == BR_STATE_DISABLED)) {
++              kfree_skb(skb);
++              return 0;
++      }
++
++      br = p->br;
+       /* insert into forwarding database after filtering to avoid spoofing */
+       br_fdb_update(p->br, p, eth_hdr(skb)->h_source);
+Index: linux-2.6.15.3/net/bridge/br_stp_bpdu.c
+===================================================================
+--- linux-2.6.15.3.orig/net/bridge/br_stp_bpdu.c
++++ linux-2.6.15.3/net/bridge/br_stp_bpdu.c
+@@ -136,10 +136,13 @@ static const unsigned char header[6] = {
+ /* NO locks */
+ int br_stp_handle_bpdu(struct sk_buff *skb)
+ {
+-      struct net_bridge_port *p = skb->dev->br_port;
+-      struct net_bridge *br = p->br;
++      struct net_bridge_port *p = rcu_dereference(skb->dev->br_port);
++      struct net_bridge *br;
+       unsigned char *buf;
++      if (!p)
++              goto err;
++
+       /* insert into forwarding database after filtering to avoid spoofing */
+       br_fdb_update(p->br, p, eth_hdr(skb)->h_source);
+@@ -150,6 +153,7 @@ int br_stp_handle_bpdu(struct sk_buff *s
+       buf = skb_pull(skb, sizeof(header));
++      br = p->br;
+       spin_lock_bh(&br->lock);
+       if (p->state == BR_STATE_DISABLED 
+           || !(br->dev->flags & IFF_UP)
diff --git a/queue/bridge-netfilter-races-on-device-removal.patch b/queue/bridge-netfilter-races-on-device-removal.patch
new file mode 100644 (file)
index 0000000..395a47f
--- /dev/null
@@ -0,0 +1,182 @@
+From stable-bounces@linux.kernel.org  Mon Feb  6 15:49:07 2006
+Date: Mon, 6 Feb 2006 15:41:15 -0800
+From: Stephen Hemminger <shemminger@osdl.org>
+To: stable@kernel.org
+Cc: netdev@vger.kernel.org, David Miller <davem@davemloft.net>
+Subject: [PATCH] bridge: netfilter races on device removal
+
+Fix bridge netfilter to handle case where interface is deleted
+from bridge while packet is being processed (on other CPU).
+
+Fixes: http://bugzilla.kernel.org/show_bug.cgi?id=5803
+
+Signed-off-by: Stephen Hemminger <shemminger@osdl.org>
+Signed-off-by: Chris Wright <chrisw@sous-sol.org>
+---
+
+ net/bridge/br_netfilter.c |   55 +++++++++++++++++++++++++++++++---------------
+ 1 files changed, 38 insertions(+), 17 deletions(-)
+
+Index: linux-2.6.15.3/net/bridge/br_netfilter.c
+===================================================================
+--- linux-2.6.15.3.orig/net/bridge/br_netfilter.c
++++ linux-2.6.15.3/net/bridge/br_netfilter.c
+@@ -47,9 +47,6 @@
+ #define store_orig_dstaddr(skb)        (skb_origaddr(skb) = (skb)->nh.iph->daddr)
+ #define dnat_took_place(skb)   (skb_origaddr(skb) != (skb)->nh.iph->daddr)
+-#define has_bridge_parent(device)     ((device)->br_port != NULL)
+-#define bridge_parent(device)         ((device)->br_port->br->dev)
+-
+ #ifdef CONFIG_SYSCTL
+ static struct ctl_table_header *brnf_sysctl_header;
+ static int brnf_call_iptables = 1;
+@@ -94,6 +91,12 @@ static struct rtable __fake_rtable = {
+       .rt_flags       = 0,
+ };
++static inline struct net_device *bridge_parent(const struct net_device *dev)
++{
++      struct net_bridge_port *port = rcu_dereference(dev->br_port);
++
++      return port ? port->br->dev : NULL;
++}
+ /* PF_BRIDGE/PRE_ROUTING *********************************************/
+ /* Undo the changes made for ip6tables PREROUTING and continue the
+@@ -185,11 +188,15 @@ static int br_nf_pre_routing_finish_brid
+       skb->nf_bridge->mask ^= BRNF_NF_BRIDGE_PREROUTING;
+       skb->dev = bridge_parent(skb->dev);
+-      if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
+-              skb_pull(skb, VLAN_HLEN);
+-              skb->nh.raw += VLAN_HLEN;
++      if (!skb->dev)
++              kfree_skb(skb);
++      else {
++              if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
++                      skb_pull(skb, VLAN_HLEN);
++                      skb->nh.raw += VLAN_HLEN;
++              }
++              skb->dst->output(skb);
+       }
+-      skb->dst->output(skb);
+       return 0;
+ }
+@@ -266,7 +273,7 @@ bridged_dnat:
+ }
+ /* Some common code for IPv4/IPv6 */
+-static void setup_pre_routing(struct sk_buff *skb)
++static struct net_device *setup_pre_routing(struct sk_buff *skb)
+ {
+       struct nf_bridge_info *nf_bridge = skb->nf_bridge;
+@@ -278,6 +285,8 @@ static void setup_pre_routing(struct sk_
+       nf_bridge->mask |= BRNF_NF_BRIDGE_PREROUTING;
+       nf_bridge->physindev = skb->dev;
+       skb->dev = bridge_parent(skb->dev);
++
++      return skb->dev;
+ }
+ /* We only check the length. A bridge shouldn't do any hop-by-hop stuff anyway */
+@@ -372,7 +381,8 @@ static unsigned int br_nf_pre_routing_ip
+       nf_bridge_put(skb->nf_bridge);
+       if ((nf_bridge = nf_bridge_alloc(skb)) == NULL)
+               return NF_DROP;
+-      setup_pre_routing(skb);
++      if (!setup_pre_routing(skb))
++              return NF_DROP;
+       NF_HOOK(PF_INET6, NF_IP6_PRE_ROUTING, skb, skb->dev, NULL,
+               br_nf_pre_routing_finish_ipv6);
+@@ -409,7 +419,6 @@ static unsigned int br_nf_pre_routing(un
+               if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
+                       skb_pull(skb, VLAN_HLEN);
+-                      (skb)->nh.raw += VLAN_HLEN;
+               }
+               return br_nf_pre_routing_ipv6(hook, skb, in, out, okfn);
+       }
+@@ -426,7 +435,6 @@ static unsigned int br_nf_pre_routing(un
+       if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
+               skb_pull(skb, VLAN_HLEN);
+-              (skb)->nh.raw += VLAN_HLEN;
+       }
+       if (!pskb_may_pull(skb, sizeof(struct iphdr)))
+@@ -456,7 +464,8 @@ static unsigned int br_nf_pre_routing(un
+       nf_bridge_put(skb->nf_bridge);
+       if ((nf_bridge = nf_bridge_alloc(skb)) == NULL)
+               return NF_DROP;
+-      setup_pre_routing(skb);
++      if (!setup_pre_routing(skb))
++              return NF_DROP;
+       store_orig_dstaddr(skb);
+       NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, skb->dev, NULL,
+@@ -530,11 +539,16 @@ static unsigned int br_nf_forward_ip(uns
+       struct sk_buff *skb = *pskb;
+       struct nf_bridge_info *nf_bridge;
+       struct vlan_ethhdr *hdr = vlan_eth_hdr(skb);
++      struct net_device *parent;
+       int pf;
+       if (!skb->nf_bridge)
+               return NF_ACCEPT;
++      parent = bridge_parent(out);
++      if (!parent)
++              return NF_DROP;
++
+       if (skb->protocol == __constant_htons(ETH_P_IP) || IS_VLAN_IP)
+               pf = PF_INET;
+       else
+@@ -555,8 +569,8 @@ static unsigned int br_nf_forward_ip(uns
+       nf_bridge->mask |= BRNF_BRIDGED;
+       nf_bridge->physoutdev = skb->dev;
+-      NF_HOOK(pf, NF_IP_FORWARD, skb, bridge_parent(in),
+-              bridge_parent(out), br_nf_forward_finish);
++      NF_HOOK(pf, NF_IP_FORWARD, skb, bridge_parent(in), parent,
++              br_nf_forward_finish);
+       return NF_STOLEN;
+ }
+@@ -679,6 +693,8 @@ static unsigned int br_nf_local_out(unsi
+               goto out;
+       }
+       realoutdev = bridge_parent(skb->dev);
++      if (!realoutdev)
++              return NF_DROP;
+ #if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
+       /* iptables should match -o br0.x */
+@@ -692,9 +708,11 @@ static unsigned int br_nf_local_out(unsi
+       /* IP forwarded traffic has a physindev, locally
+        * generated traffic hasn't. */
+       if (realindev != NULL) {
+-              if (!(nf_bridge->mask & BRNF_DONT_TAKE_PARENT) &&
+-                  has_bridge_parent(realindev))
+-                      realindev = bridge_parent(realindev);
++              if (!(nf_bridge->mask & BRNF_DONT_TAKE_PARENT) ) {
++                      struct net_device *parent = bridge_parent(realindev);
++                      if (parent)
++                              realindev = parent;
++              }
+               NF_HOOK_THRESH(pf, NF_IP_FORWARD, skb, realindev,
+                              realoutdev, br_nf_local_out_finish,
+@@ -734,6 +752,9 @@ static unsigned int br_nf_post_routing(u
+       if (!nf_bridge)
+               return NF_ACCEPT;
++      if (!realoutdev)
++              return NF_DROP;
++
+       if (skb->protocol == __constant_htons(ETH_P_IP) || IS_VLAN_IP)
+               pf = PF_INET;
+       else
index cc18735596bbc6bcf1fd2bd5e9c41094192725a6..48c634c5b90805c83f6d67013485ce4b9750e0bd 100644 (file)
@@ -16,3 +16,4 @@ selinux-fix-size-128-slab-leak.patch
 fixed-hardware-rx-checksum-handling.patch
 x86_64-let-impossible-cpus-point-to-reference-per-cpu-data.patch
 x86_64-clear-more-state-when-ignoring-empty-node-in-srat-parsing.patch
+bridge-netfilter-races-on-device-removal.patch