df43314
From 8bf862739a7786ae72409220914df960a0aa80d8 Mon Sep 17 00:00:00 2001
785c5f0
From: Johannes Berg <johannes.berg@intel.com>
df43314
Date: Wed, 27 Jan 2016 12:37:52 +0100
df43314
Subject: wext: fix message delay/ordering
785c5f0
785c5f0
Beniamino reported that he was getting an RTM_NEWLINK message for a
785c5f0
given interface, after the RTM_DELLINK for it. It turns out that the
785c5f0
message is a wireless extensions message, which was sent because the
785c5f0
interface had been connected and disconnection while it was deleted
785c5f0
caused a wext message.
785c5f0
785c5f0
For its netlink messages, wext uses RTM_NEWLINK, but the message is
785c5f0
without all the regular rtnetlink attributes, so "ip monitor link"
785c5f0
prints just rudimentary information:
785c5f0
785c5f0
5: wlan1: <BROADCAST,MULTICAST> mtu 1500 qdisc mq state DOWN group default
785c5f0
    link/ether 02:00:00:00:01:00 brd ff:ff:ff:ff:ff:ff
785c5f0
Deleted 5: wlan1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default
785c5f0
    link/ether 02:00:00:00:01:00 brd ff:ff:ff:ff:ff:ff
785c5f0
5: wlan1: <BROADCAST,MULTICAST,UP>
785c5f0
    link/ether
785c5f0
(from my hwsim reproduction)
785c5f0
785c5f0
This can cause userspace to get confused since it doesn't expect an
785c5f0
RTM_NEWLINK message after RTM_DELLINK.
785c5f0
785c5f0
The reason for this is that wext schedules a worker to send out the
785c5f0
messages, and the scheduling delay can cause the messages to get out
785c5f0
to userspace in different order.
785c5f0
785c5f0
To fix this, have wext register a netdevice notifier and flush out
785c5f0
any pending messages when netdevice state changes. This fixes any
785c5f0
ordering whenever the original message wasn't sent by a notifier
785c5f0
itself.
785c5f0
785c5f0
Cc: stable@vger.kernel.org
785c5f0
Reported-by: Beniamino Galvani <bgalvani@redhat.com>
785c5f0
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
785c5f0
---
df43314
 net/wireless/wext-core.c | 51 +++++++++++++++++++++++++++++++++++++-----------
df43314
 1 file changed, 40 insertions(+), 11 deletions(-)
785c5f0
785c5f0
diff --git a/net/wireless/wext-core.c b/net/wireless/wext-core.c
df43314
index c8717c1..87dd619 100644
785c5f0
--- a/net/wireless/wext-core.c
785c5f0
+++ b/net/wireless/wext-core.c
785c5f0
@@ -342,6 +342,39 @@ static const int compat_event_type_size[] = {
785c5f0
 
785c5f0
 /* IW event code */
785c5f0
 
785c5f0
+static void wireless_nlevent_flush(void)
785c5f0
+{
785c5f0
+	struct sk_buff *skb;
785c5f0
+	struct net *net;
785c5f0
+
785c5f0
+	ASSERT_RTNL();
785c5f0
+
785c5f0
+	for_each_net(net) {
785c5f0
+		while ((skb = skb_dequeue(&net->wext_nlevents)))
785c5f0
+			rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL,
785c5f0
+				    GFP_KERNEL);
785c5f0
+	}
785c5f0
+}
785c5f0
+
785c5f0
+static int wext_netdev_notifier_call(struct notifier_block *nb,
785c5f0
+				     unsigned long state, void *ptr)
785c5f0
+{
785c5f0
+	/*
785c5f0
+	 * When a netdev changes state in any way, flush all pending messages
785c5f0
+	 * to avoid them going out in a strange order, e.g. RTM_NEWLINK after
785c5f0
+	 * RTM_DELLINK, or with IFF_UP after without IFF_UP during dev_close()
785c5f0
+	 * or similar - all of which could otherwise happen due to delays from
785c5f0
+	 * schedule_work().
785c5f0
+	 */
785c5f0
+	wireless_nlevent_flush();
785c5f0
+
785c5f0
+	return NOTIFY_OK;
785c5f0
+}
785c5f0
+
785c5f0
+static struct notifier_block wext_netdev_notifier = {
785c5f0
+	.notifier_call = wext_netdev_notifier_call,
785c5f0
+};
785c5f0
+
785c5f0
 static int __net_init wext_pernet_init(struct net *net)
785c5f0
 {
785c5f0
 	skb_queue_head_init(&net->wext_nlevents);
df43314
@@ -360,7 +393,12 @@ static struct pernet_operations wext_pernet_ops = {
785c5f0
 
785c5f0
 static int __init wireless_nlevent_init(void)
785c5f0
 {
df43314
-	return register_pernet_subsys(&wext_pernet_ops);
df43314
+	int err = register_pernet_subsys(&wext_pernet_ops);
785c5f0
+
785c5f0
+	if (err)
785c5f0
+		return err;
785c5f0
+
df43314
+	return register_netdevice_notifier(&wext_netdev_notifier);
785c5f0
 }
785c5f0
 
df43314
 subsys_initcall(wireless_nlevent_init);
785c5f0
@@ -368,17 +406,8 @@ subsys_initcall(wireless_nlevent_init);
785c5f0
 /* Process events generated by the wireless layer or the driver. */
785c5f0
 static void wireless_nlevent_process(struct work_struct *work)
785c5f0
 {
785c5f0
-	struct sk_buff *skb;
785c5f0
-	struct net *net;
785c5f0
-
785c5f0
 	rtnl_lock();
785c5f0
-
785c5f0
-	for_each_net(net) {
785c5f0
-		while ((skb = skb_dequeue(&net->wext_nlevents)))
785c5f0
-			rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL,
785c5f0
-				    GFP_KERNEL);
785c5f0
-	}
785c5f0
-
785c5f0
+	wireless_nlevent_flush();
785c5f0
 	rtnl_unlock();
785c5f0
 }
785c5f0
 
785c5f0
-- 
df43314
cgit v0.12
785c5f0