summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStephen Hemminger <shemminger@osdl.org>2006-02-06 15:42:45 -0800
committerChris Wright <chrisw@sous-sol.org>2006-02-09 23:20:15 -0800
commita426fa9147f034d5355875ffcb91f0a61e5b393a (patch)
tree9ae78d4cf9ae05fc71183e1b3a23244dc2cd3bc2
parentec81e3178071f3747bd1522c959972105584514b (diff)
[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> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r--net/bridge/br_if.c7
-rw-r--r--net/bridge/br_input.c10
-rw-r--r--net/bridge/br_stp_bpdu.c8
3 files changed, 16 insertions, 9 deletions
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index 975abe254b7a..c085d7586e99 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -99,7 +99,6 @@ static void del_nbp(struct net_bridge_port *p)
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_port *p)
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(struct net_bridge *br,
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, struct net_device *dev)
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);
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c
index b88220a64cd8..c027ac3b9043 100644
--- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c
@@ -45,11 +45,17 @@ static void br_pass_frame_up(struct net_bridge *br, struct sk_buff *skb)
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);
diff --git a/net/bridge/br_stp_bpdu.c b/net/bridge/br_stp_bpdu.c
index d071f1c9ad0b..78b8f2806359 100644
--- a/net/bridge/br_stp_bpdu.c
+++ b/net/bridge/br_stp_bpdu.c
@@ -136,10 +136,13 @@ static const unsigned char header[6] = {0x42, 0x42, 0x03, 0x00, 0x00, 0x00};
/* 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 *skb)
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)