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