diff options
author | Stephen Hemminger <shemminger@osdl.org> | 2006-02-09 17:08:52 -0800 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2006-02-09 17:08:52 -0800 |
commit | b3f1be4b5412e34647764457bec901e06b03e624 (patch) | |
tree | b3f1529ff0882ebb5aed190d8a61b52f703503ce /net/bridge/br_input.c | |
parent | 6fcf9412de64056238a6295f21db7aa9c37a532e (diff) | |
download | linux-b3f1be4b5412e34647764457bec901e06b03e624.tar.bz2 |
[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.c | 19 |
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; } /* |