summaryrefslogtreecommitdiffstats
path: root/net/bridge/br_input.c
diff options
context:
space:
mode:
authorStephen Hemminger <shemminger@osdl.org>2006-02-10 02:08:52 +0100
committerDavid S. Miller <davem@davemloft.net>2006-02-10 02:08:52 +0100
commitb3f1be4b5412e34647764457bec901e06b03e624 (patch)
treeb3f1529ff0882ebb5aed190d8a61b52f703503ce /net/bridge/br_input.c
parent[TCP]: rcvbuf lock when tcp_moderate_rcvbuf enabled (diff)
downloadlinux-b3f1be4b5412e34647764457bec901e06b03e624.tar.xz
linux-b3f1be4b5412e34647764457bec901e06b03e624.zip
[BRIDGE]: fix for RCU and deadlock on device removal
Change Bridge receive path to correctly handle RCU removal of device from bridge. Also fixes deadlock between carrier_check and del_nbp. This replaces the previous deleted flag fix. Signed-off-by: Stephen Hemminger <shemminger@osdl.org> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/bridge/br_input.c')
-rw-r--r--net/bridge/br_input.c19
1 files changed, 12 insertions, 7 deletions
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c
index e3a73cead6b6..4eef83755315 100644
--- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c
@@ -45,18 +45,20 @@ 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 (!p || p->state == BR_STATE_DISABLED)
+ goto drop;
+
/* insert into forwarding database after filtering to avoid spoofing */
- br_fdb_update(p->br, p, eth_hdr(skb)->h_source);
+ br = p->br;
+ br_fdb_update(br, p, eth_hdr(skb)->h_source);
- if (p->state == BR_STATE_LEARNING) {
- kfree_skb(skb);
- goto out;
- }
+ if (p->state == BR_STATE_LEARNING)
+ goto drop;
if (br->dev->flags & IFF_PROMISC) {
struct sk_buff *skb2;
@@ -93,6 +95,9 @@ int br_handle_frame_finish(struct sk_buff *skb)
out:
return 0;
+drop:
+ kfree_skb(skb);
+ goto out;
}
/*