aboutsummaryrefslogtreecommitdiffstats
path: root/net/bridge/br_device.c
diff options
context:
space:
mode:
authorHerbert Xu <herbert@gondor.apana.org.au>2010-06-10 12:12:50 -0400
committerDavid S. Miller <davem@davemloft.net>2010-06-15 14:00:40 -0400
commit91d2c34a4eed32876ca333b0ca44f3bc56645805 (patch)
tree26799e5cb1dff486a0695e1be8618064d12fe0bd /net/bridge/br_device.c
parentc18370f5b2949d9cca519355f33690b75e1e7c8b (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_device.c')
-rw-r--r--net/bridge/br_device.c108
1 files changed, 61 insertions, 47 deletions
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
index f8cb908db81f..6f3a9279be30 100644
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -47,6 +47,10 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
47 skb_pull(skb, ETH_HLEN); 47 skb_pull(skb, ETH_HLEN);
48 48
49 if (is_multicast_ether_addr(dest)) { 49 if (is_multicast_ether_addr(dest)) {
50 if (unlikely(netpoll_tx_running(dev))) {
51 br_flood_deliver(br, skb);
52 goto out;
53 }
50 if (br_multicast_rcv(br, NULL, skb)) 54 if (br_multicast_rcv(br, NULL, skb))
51 goto out; 55 goto out;
52 56
@@ -199,72 +203,81 @@ static int br_set_tx_csum(struct net_device *dev, u32 data)
199} 203}
200 204
201#ifdef CONFIG_NET_POLL_CONTROLLER 205#ifdef CONFIG_NET_POLL_CONTROLLER
202static bool br_devices_support_netpoll(struct net_bridge *br) 206static void br_poll_controller(struct net_device *br_dev)
203{ 207{
204 struct net_bridge_port *p;
205 bool ret = true;
206 int count = 0;
207 unsigned long flags;
208
209 spin_lock_irqsave(&br->lock, flags);
210 list_for_each_entry(p, &br->port_list, list) {
211 count++;
212 if ((p->dev->priv_flags & IFF_DISABLE_NETPOLL) ||
213 !p->dev->netdev_ops->ndo_poll_controller)
214 ret = false;
215 }
216 spin_unlock_irqrestore(&br->lock, flags);
217 return count != 0 && ret;
218} 208}
219 209
220static void br_poll_controller(struct net_device *br_dev) 210static void br_netpoll_cleanup(struct net_device *dev)
221{ 211{
222 struct netpoll *np = br_dev->npinfo->netpoll; 212 struct net_bridge *br = netdev_priv(dev);
213 struct net_bridge_port *p, *n;
223 214
224 if (np->real_dev != br_dev) 215 list_for_each_entry_safe(p, n, &br->port_list, list) {
225 netpoll_poll_dev(np->real_dev); 216 br_netpoll_disable(p);
217 }
226} 218}
227 219
228void br_netpoll_cleanup(struct net_device *dev) 220static int br_netpoll_setup(struct net_device *dev, struct netpoll_info *ni)
229{ 221{
230 struct net_bridge *br = netdev_priv(dev); 222 struct net_bridge *br = netdev_priv(dev);
231 struct net_bridge_port *p, *n; 223 struct net_bridge_port *p, *n;
232 const struct net_device_ops *ops; 224 int err = 0;
233 225
234 list_for_each_entry_safe(p, n, &br->port_list, list) { 226 list_for_each_entry_safe(p, n, &br->port_list, list) {
235 if (p->dev) { 227 if (!p->dev)
236 ops = p->dev->netdev_ops; 228 continue;
237 if (ops->ndo_netpoll_cleanup) 229
238 ops->ndo_netpoll_cleanup(p->dev); 230 err = br_netpoll_enable(p);
239 else 231 if (err)
240 p->dev->npinfo = NULL; 232 goto fail;
241 }
242 } 233 }
234
235out:
236 return err;
237
238fail:
239 br_netpoll_cleanup(dev);
240 goto out;
243} 241}
244 242
245void br_netpoll_disable(struct net_bridge *br, 243int br_netpoll_enable(struct net_bridge_port *p)
246 struct net_device *dev)
247{ 244{
248 if (br_devices_support_netpoll(br)) 245 struct netpoll *np;
249 br->dev->priv_flags &= ~IFF_DISABLE_NETPOLL; 246 int err = 0;
250 if (dev->netdev_ops->ndo_netpoll_cleanup) 247
251 dev->netdev_ops->ndo_netpoll_cleanup(dev); 248 np = kzalloc(sizeof(*p->np), GFP_KERNEL);
252 else 249 err = -ENOMEM;
253 dev->npinfo = NULL; 250 if (!np)
251 goto out;
252
253 np->dev = p->dev;
254
255 err = __netpoll_setup(np);
256 if (err) {
257 kfree(np);
258 goto out;
259 }
260
261 p->np = np;
262
263out:
264 return err;
254} 265}
255 266
256void br_netpoll_enable(struct net_bridge *br, 267void br_netpoll_disable(struct net_bridge_port *p)
257 struct net_device *dev)
258{ 268{
259 if (br_devices_support_netpoll(br)) { 269 struct netpoll *np = p->np;
260 br->dev->priv_flags &= ~IFF_DISABLE_NETPOLL; 270
261 if (br->dev->npinfo) 271 if (!np)
262 dev->npinfo = br->dev->npinfo; 272 return;
263 } else if (!(br->dev->priv_flags & IFF_DISABLE_NETPOLL)) { 273
264 br->dev->priv_flags |= IFF_DISABLE_NETPOLL; 274 p->np = NULL;
265 br_info(br,"new device %s does not support netpoll (disabling)", 275
266 dev->name); 276 /* Wait for transmitting packets to finish before freeing. */
267 } 277 synchronize_rcu_bh();
278
279 __netpoll_cleanup(np);
280 kfree(np);
268} 281}
269 282
270#endif 283#endif
@@ -293,6 +306,7 @@ static const struct net_device_ops br_netdev_ops = {
293 .ndo_change_mtu = br_change_mtu, 306 .ndo_change_mtu = br_change_mtu,
294 .ndo_do_ioctl = br_dev_ioctl, 307 .ndo_do_ioctl = br_dev_ioctl,
295#ifdef CONFIG_NET_POLL_CONTROLLER 308#ifdef CONFIG_NET_POLL_CONTROLLER
309 .ndo_netpoll_setup = br_netpoll_setup,
296 .ndo_netpoll_cleanup = br_netpoll_cleanup, 310 .ndo_netpoll_cleanup = br_netpoll_cleanup,
297 .ndo_poll_controller = br_poll_controller, 311 .ndo_poll_controller = br_poll_controller,
298#endif 312#endif