aboutsummaryrefslogtreecommitdiffstats
path: root/net/bridge
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
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')
-rw-r--r--net/bridge/br_device.c108
-rw-r--r--net/bridge/br_forward.c34
-rw-r--r--net/bridge/br_if.c16
-rw-r--r--net/bridge/br_private.h46
4 files changed, 120 insertions, 84 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
diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c
index a98ef1393097..6e97711fd2c5 100644
--- a/net/bridge/br_forward.c
+++ b/net/bridge/br_forward.c
@@ -50,14 +50,7 @@ int br_dev_queue_push_xmit(struct sk_buff *skb)
50 kfree_skb(skb); 50 kfree_skb(skb);
51 else { 51 else {
52 skb_push(skb, ETH_HLEN); 52 skb_push(skb, ETH_HLEN);
53 53 dev_queue_xmit(skb);
54#ifdef CONFIG_NET_POLL_CONTROLLER
55 if (unlikely(skb->dev->priv_flags & IFF_IN_NETPOLL)) {
56 netpoll_send_skb(skb->dev->npinfo->netpoll, skb);
57 skb->dev->priv_flags &= ~IFF_IN_NETPOLL;
58 } else
59#endif
60 dev_queue_xmit(skb);
61 } 54 }
62 } 55 }
63 56
@@ -73,23 +66,20 @@ int br_forward_finish(struct sk_buff *skb)
73 66
74static void __br_deliver(const struct net_bridge_port *to, struct sk_buff *skb) 67static void __br_deliver(const struct net_bridge_port *to, struct sk_buff *skb)
75{ 68{
76#ifdef CONFIG_NET_POLL_CONTROLLER
77 struct net_bridge *br = to->br;
78 if (unlikely(br->dev->priv_flags & IFF_IN_NETPOLL)) {
79 struct netpoll *np;
80 to->dev->npinfo = skb->dev->npinfo;
81 np = skb->dev->npinfo->netpoll;
82 np->real_dev = np->dev = to->dev;
83 to->dev->priv_flags |= IFF_IN_NETPOLL;
84 }
85#endif
86 skb->dev = to->dev; 69 skb->dev = to->dev;
70
71 if (unlikely(netpoll_tx_running(to->dev))) {
72 if (packet_length(skb) > skb->dev->mtu && !skb_is_gso(skb))
73 kfree_skb(skb);
74 else {
75 skb_push(skb, ETH_HLEN);
76 br_netpoll_send_skb(to, skb);
77 }
78 return;
79 }
80
87 NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev, 81 NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
88 br_forward_finish); 82 br_forward_finish);
89#ifdef CONFIG_NET_POLL_CONTROLLER
90 if (skb->dev->npinfo)
91 skb->dev->npinfo->netpoll->dev = br->dev;
92#endif
93} 83}
94 84
95static void __br_forward(const struct net_bridge_port *to, struct sk_buff *skb) 85static void __br_forward(const struct net_bridge_port *to, struct sk_buff *skb)
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)
155 kobject_uevent(&p->kobj, KOBJ_REMOVE); 155 kobject_uevent(&p->kobj, KOBJ_REMOVE);
156 kobject_del(&p->kobj); 156 kobject_del(&p->kobj);
157 157
158 br_netpoll_disable(br, dev); 158 br_netpoll_disable(p);
159
159 call_rcu(&p->rcu, destroy_nbp_rcu); 160 call_rcu(&p->rcu, destroy_nbp_rcu);
160} 161}
161 162
@@ -168,8 +169,6 @@ static void del_br(struct net_bridge *br, struct list_head *head)
168 del_nbp(p); 169 del_nbp(p);
169 } 170 }
170 171
171 br_netpoll_cleanup(br->dev);
172
173 del_timer_sync(&br->gc_timer); 172 del_timer_sync(&br->gc_timer);
174 173
175 br_sysfs_delbr(br->dev); 174 br_sysfs_delbr(br->dev);
@@ -429,11 +428,14 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
429 if (err) 428 if (err)
430 goto err2; 429 goto err2;
431 430
431 if (br_netpoll_info(br) && ((err = br_netpoll_enable(p))))
432 goto err3;
433
432 rcu_assign_pointer(dev->br_port, p); 434 rcu_assign_pointer(dev->br_port, p);
433 435
434 err = netdev_rx_handler_register(dev, br_handle_frame); 436 err = netdev_rx_handler_register(dev, br_handle_frame);
435 if (err) 437 if (err)
436 goto err3; 438 goto err4;
437 439
438 dev_disable_lro(dev); 440 dev_disable_lro(dev);
439 441
@@ -454,11 +456,11 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
454 456
455 kobject_uevent(&p->kobj, KOBJ_ADD); 457 kobject_uevent(&p->kobj, KOBJ_ADD);
456 458
457 br_netpoll_enable(br, dev);
458
459 return 0; 459 return 0;
460err3: 460err4:
461 rcu_assign_pointer(dev->br_port, NULL); 461 rcu_assign_pointer(dev->br_port, NULL);
462err3:
463 sysfs_remove_link(br->ifobj, p->dev->name);
462err2: 464err2:
463 br_fdb_delete_by_port(br, p, 1); 465 br_fdb_delete_by_port(br, p, 1);
464err1: 466err1:
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index c83519b555bb..0f5394c4f2f1 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -15,6 +15,7 @@
15 15
16#include <linux/netdevice.h> 16#include <linux/netdevice.h>
17#include <linux/if_bridge.h> 17#include <linux/if_bridge.h>
18#include <linux/netpoll.h>
18#include <net/route.h> 19#include <net/route.h>
19 20
20#define BR_HASH_BITS 8 21#define BR_HASH_BITS 8
@@ -143,6 +144,10 @@ struct net_bridge_port
143#ifdef CONFIG_SYSFS 144#ifdef CONFIG_SYSFS
144 char sysfs_name[IFNAMSIZ]; 145 char sysfs_name[IFNAMSIZ];
145#endif 146#endif
147
148#ifdef CONFIG_NET_POLL_CONTROLLER
149 struct netpoll *np;
150#endif
146}; 151};
147 152
148struct br_cpu_netstats { 153struct br_cpu_netstats {
@@ -273,16 +278,41 @@ extern void br_dev_setup(struct net_device *dev);
273extern netdev_tx_t br_dev_xmit(struct sk_buff *skb, 278extern netdev_tx_t br_dev_xmit(struct sk_buff *skb,
274 struct net_device *dev); 279 struct net_device *dev);
275#ifdef CONFIG_NET_POLL_CONTROLLER 280#ifdef CONFIG_NET_POLL_CONTROLLER
276extern void br_netpoll_cleanup(struct net_device *dev); 281static inline struct netpoll_info *br_netpoll_info(struct net_bridge *br)
277extern void br_netpoll_enable(struct net_bridge *br, 282{
278 struct net_device *dev); 283 return br->dev->npinfo;
279extern void br_netpoll_disable(struct net_bridge *br, 284}
280 struct net_device *dev); 285
286static inline void br_netpoll_send_skb(const struct net_bridge_port *p,
287 struct sk_buff *skb)
288{
289 struct netpoll *np = p->np;
290
291 if (np)
292 netpoll_send_skb(np, skb);
293}
294
295extern int br_netpoll_enable(struct net_bridge_port *p);
296extern void br_netpoll_disable(struct net_bridge_port *p);
281#else 297#else
282#define br_netpoll_cleanup(br) 298static inline struct netpoll_info *br_netpoll_info(struct net_bridge *br)
283#define br_netpoll_enable(br, dev) 299{
284#define br_netpoll_disable(br, dev) 300 return NULL;
301}
302
303static inline void br_netpoll_send_skb(struct net_bridge_port *p,
304 struct sk_buff *skb)
305{
306}
285 307
308static inline int br_netpoll_enable(struct net_bridge_port *p)
309{
310 return 0;
311}
312
313static inline void br_netpoll_disable(struct net_bridge_port *p)
314{
315}
286#endif 316#endif
287 317
288/* br_fdb.c */ 318/* br_fdb.c */