diff options
author | Herbert Xu <herbert@gondor.apana.org.au> | 2010-06-10 16:12:50 +0000 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-06-15 11:00:40 -0700 |
commit | 91d2c34a4eed32876ca333b0ca44f3bc56645805 (patch) | |
tree | 26799e5cb1dff486a0695e1be8618064d12fe0bd /net/bridge/br_if.c | |
parent | c18370f5b2949d9cca519355f33690b75e1e7c8b (diff) |
bridge: Fix netpoll support
There are multiple problems with the newly added netpoll support:
1) Use-after-free on each netpoll packet.
2) Invoking unsafe code on netpoll/IRQ path.
3) Breaks when netpoll is enabled on the underlying device.
This patch fixes all of these problems. In particular, we now
allocate proper netpoll structures for each underlying device.
We only allow netpoll to be enabled on the bridge when all the
devices underneath it support netpoll. Once it is enabled, we
do not allow non-netpoll devices to join the bridge (until netpoll
is disabled again).
This allows us to do away with the npinfo juggling that caused
problem number 1.
Incidentally this patch fixes number 2 by bypassing unsafe code
such as multicast snooping and netfilter.
Reported-by: Qianfeng Zhang <frzhang@redhat.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/bridge/br_if.c')
-rw-r--r-- | net/bridge/br_if.c | 16 |
1 files changed, 9 insertions, 7 deletions
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index d9242342837e..97ac9da4d76c 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -155,7 +155,8 @@ static void del_nbp(struct net_bridge_port *p) kobject_uevent(&p->kobj, KOBJ_REMOVE); kobject_del(&p->kobj); - br_netpoll_disable(br, dev); + br_netpoll_disable(p); + call_rcu(&p->rcu, destroy_nbp_rcu); } @@ -168,8 +169,6 @@ static void del_br(struct net_bridge *br, struct list_head *head) del_nbp(p); } - br_netpoll_cleanup(br->dev); - del_timer_sync(&br->gc_timer); br_sysfs_delbr(br->dev); @@ -429,11 +428,14 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) if (err) goto err2; + if (br_netpoll_info(br) && ((err = br_netpoll_enable(p)))) + goto err3; + rcu_assign_pointer(dev->br_port, p); err = netdev_rx_handler_register(dev, br_handle_frame); if (err) - goto err3; + goto err4; dev_disable_lro(dev); @@ -454,11 +456,11 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) kobject_uevent(&p->kobj, KOBJ_ADD); - br_netpoll_enable(br, dev); - return 0; -err3: +err4: rcu_assign_pointer(dev->br_port, NULL); +err3: + sysfs_remove_link(br->ifobj, p->dev->name); err2: br_fdb_delete_by_port(br, p, 1); err1: |