diff options
author | Johannes Berg <johannes.berg@intel.com> | 2016-01-27 06:37:52 -0500 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2016-01-29 11:13:39 -0500 |
commit | 8bf862739a7786ae72409220914df960a0aa80d8 (patch) | |
tree | 8ee53c9ca892727cf0623fd7aabb25d242cdfee6 /net/wireless | |
parent | 6736fde9672ff6717ac576e9bba2fd5f3dfec822 (diff) |
wext: fix message delay/ordering
Beniamino reported that he was getting an RTM_NEWLINK message for a
given interface, after the RTM_DELLINK for it. It turns out that the
message is a wireless extensions message, which was sent because the
interface had been connected and disconnection while it was deleted
caused a wext message.
For its netlink messages, wext uses RTM_NEWLINK, but the message is
without all the regular rtnetlink attributes, so "ip monitor link"
prints just rudimentary information:
5: wlan1: <BROADCAST,MULTICAST> mtu 1500 qdisc mq state DOWN group default
link/ether 02:00:00:00:01:00 brd ff:ff:ff:ff:ff:ff
Deleted 5: wlan1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default
link/ether 02:00:00:00:01:00 brd ff:ff:ff:ff:ff:ff
5: wlan1: <BROADCAST,MULTICAST,UP>
link/ether
(from my hwsim reproduction)
This can cause userspace to get confused since it doesn't expect an
RTM_NEWLINK message after RTM_DELLINK.
The reason for this is that wext schedules a worker to send out the
messages, and the scheduling delay can cause the messages to get out
to userspace in different order.
To fix this, have wext register a netdevice notifier and flush out
any pending messages when netdevice state changes. This fixes any
ordering whenever the original message wasn't sent by a notifier
itself.
Cc: stable@vger.kernel.org
Reported-by: Beniamino Galvani <bgalvani@redhat.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/wireless')
-rw-r--r-- | net/wireless/wext-core.c | 51 |
1 files changed, 40 insertions, 11 deletions
diff --git a/net/wireless/wext-core.c b/net/wireless/wext-core.c index c8717c1d082e..87dd619fb2e9 100644 --- a/net/wireless/wext-core.c +++ b/net/wireless/wext-core.c | |||
@@ -342,6 +342,39 @@ static const int compat_event_type_size[] = { | |||
342 | 342 | ||
343 | /* IW event code */ | 343 | /* IW event code */ |
344 | 344 | ||
345 | static void wireless_nlevent_flush(void) | ||
346 | { | ||
347 | struct sk_buff *skb; | ||
348 | struct net *net; | ||
349 | |||
350 | ASSERT_RTNL(); | ||
351 | |||
352 | for_each_net(net) { | ||
353 | while ((skb = skb_dequeue(&net->wext_nlevents))) | ||
354 | rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, | ||
355 | GFP_KERNEL); | ||
356 | } | ||
357 | } | ||
358 | |||
359 | static int wext_netdev_notifier_call(struct notifier_block *nb, | ||
360 | unsigned long state, void *ptr) | ||
361 | { | ||
362 | /* | ||
363 | * When a netdev changes state in any way, flush all pending messages | ||
364 | * to avoid them going out in a strange order, e.g. RTM_NEWLINK after | ||
365 | * RTM_DELLINK, or with IFF_UP after without IFF_UP during dev_close() | ||
366 | * or similar - all of which could otherwise happen due to delays from | ||
367 | * schedule_work(). | ||
368 | */ | ||
369 | wireless_nlevent_flush(); | ||
370 | |||
371 | return NOTIFY_OK; | ||
372 | } | ||
373 | |||
374 | static struct notifier_block wext_netdev_notifier = { | ||
375 | .notifier_call = wext_netdev_notifier_call, | ||
376 | }; | ||
377 | |||
345 | static int __net_init wext_pernet_init(struct net *net) | 378 | static int __net_init wext_pernet_init(struct net *net) |
346 | { | 379 | { |
347 | skb_queue_head_init(&net->wext_nlevents); | 380 | skb_queue_head_init(&net->wext_nlevents); |
@@ -360,7 +393,12 @@ static struct pernet_operations wext_pernet_ops = { | |||
360 | 393 | ||
361 | static int __init wireless_nlevent_init(void) | 394 | static int __init wireless_nlevent_init(void) |
362 | { | 395 | { |
363 | return register_pernet_subsys(&wext_pernet_ops); | 396 | int err = register_pernet_subsys(&wext_pernet_ops); |
397 | |||
398 | if (err) | ||
399 | return err; | ||
400 | |||
401 | return register_netdevice_notifier(&wext_netdev_notifier); | ||
364 | } | 402 | } |
365 | 403 | ||
366 | subsys_initcall(wireless_nlevent_init); | 404 | subsys_initcall(wireless_nlevent_init); |
@@ -368,17 +406,8 @@ subsys_initcall(wireless_nlevent_init); | |||
368 | /* Process events generated by the wireless layer or the driver. */ | 406 | /* Process events generated by the wireless layer or the driver. */ |
369 | static void wireless_nlevent_process(struct work_struct *work) | 407 | static void wireless_nlevent_process(struct work_struct *work) |
370 | { | 408 | { |
371 | struct sk_buff *skb; | ||
372 | struct net *net; | ||
373 | |||
374 | rtnl_lock(); | 409 | rtnl_lock(); |
375 | 410 | wireless_nlevent_flush(); | |
376 | for_each_net(net) { | ||
377 | while ((skb = skb_dequeue(&net->wext_nlevents))) | ||
378 | rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, | ||
379 | GFP_KERNEL); | ||
380 | } | ||
381 | |||
382 | rtnl_unlock(); | 411 | rtnl_unlock(); |
383 | } | 412 | } |
384 | 413 | ||