Disconnect or deauthenticate when the owning socket is closed if this flag has been supplied to CMD_CONNECT, CMD_AUTHENTICATE or CMD_ASSOCIATE. Signed-off-by: Andrew Zaborowski <andrew.zaborowski@xxxxxxxxx> --- include/net/cfg80211.h | 5 +++++ include/uapi/linux/nl80211.h | 3 +++ net/wireless/core.c | 23 +++++++++++++++++++++++ net/wireless/mlme.c | 4 ++++ net/wireless/nl80211.c | 29 ++++++++++++++++++++++++++++- net/wireless/sme.c | 4 ++++ 6 files changed, 67 insertions(+), 1 deletion(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index bd19faa..413f5b5 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -3764,6 +3764,8 @@ struct cfg80211_cached_keys; * @conn: (private) cfg80211 software SME connection state machine data * @connect_keys: (private) keys to set after connection is established * @conn_bss_type: connecting/connected BSS type + * @conn_owner_nlportid: (private) connection owner socket port ID + * @disconnect_wk: (private) auto-disconnect work * @ibss_fixed: (private) IBSS is using fixed BSSID * @ibss_dfs_possible: (private) IBSS may change to a DFS channel * @event_list: (private) list for internal event processing @@ -3795,6 +3797,9 @@ struct wireless_dev { struct cfg80211_conn *conn; struct cfg80211_cached_keys *connect_keys; enum ieee80211_bss_type conn_bss_type; + u32 conn_owner_nlportid; + + struct work_struct disconnect_wk; struct list_head event_list; spinlock_t event_lock; diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 56368e9..12f41b0 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1788,6 +1788,9 @@ enum nl80211_commands { * and remove functions. NAN notifications will be sent in unicast to that * socket. Without this attribute, any socket can add functions and the * notifications will be sent to the %NL80211_MCGRP_NAN multicast group. + * If set during one of: %NL80211_CMD_AUTHENTICATE, %NL80211_CMD_ASSOCIATE + * or %NL80211_CMD_CONNECT the station will deauthenticate when the + * socket is closed. * * @NL80211_ATTR_TDLS_INITIATOR: flag attribute indicating the current end is * the TDLS link initiator. diff --git a/net/wireless/core.c b/net/wireless/core.c index 8201e6d..98db6b2 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -357,6 +357,26 @@ static void cfg80211_sched_scan_stop_wk(struct work_struct *work) rtnl_unlock(); } +static void cfg80211_disconnect_wk(struct work_struct *work) +{ + struct cfg80211_registered_device *rdev; + struct wireless_dev *wdev; + + wdev = container_of(work, struct wireless_dev, disconnect_wk); + rdev = wiphy_to_rdev(wdev->wiphy); + + if (!wdev->netdev) + return; + + wdev_lock(wdev); + + if (wdev->conn_owner_nlportid) + cfg80211_disconnect(rdev, wdev->netdev, + WLAN_REASON_DEAUTH_LEAVING, true); + + wdev_unlock(wdev); +} + /* exported functions */ struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv, @@ -1117,6 +1137,8 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, wdev->iftype == NL80211_IFTYPE_ADHOC) && !wdev->use_4addr) dev->priv_flags |= IFF_DONT_BRIDGE; + INIT_WORK(&wdev->disconnect_wk, cfg80211_disconnect_wk); + nl80211_notify_iface(rdev, wdev, NL80211_CMD_NEW_INTERFACE); break; case NETDEV_GOING_DOWN: @@ -1205,6 +1227,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, #ifdef CONFIG_CFG80211_WEXT kzfree(wdev->wext.keys); #endif + flush_work(&wdev->disconnect_wk); } /* * synchronise (so that we won't find this netdev diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index cbb48e2..eaf2d1d 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -130,6 +130,8 @@ void cfg80211_auth_timeout(struct net_device *dev, const u8 *addr) nl80211_send_auth_timeout(rdev, dev, addr, GFP_KERNEL); cfg80211_sme_auth_timeout(wdev); + + wdev->conn_owner_nlportid = 0; } EXPORT_SYMBOL(cfg80211_auth_timeout); @@ -146,6 +148,8 @@ void cfg80211_assoc_timeout(struct net_device *dev, struct cfg80211_bss *bss) cfg80211_unhold_bss(bss_from_pub(bss)); cfg80211_put_bss(wiphy, bss); + + wdev->conn_owner_nlportid = 0; } EXPORT_SYMBOL(cfg80211_assoc_timeout); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index c510810..ccd74c7 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -7818,6 +7818,10 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) key.p.key, key.p.key_len, key.idx, sae_data, sae_data_len); wdev_unlock(dev->ieee80211_ptr); + + if (!err && info->attrs[NL80211_ATTR_SOCKET_OWNER]) + dev->ieee80211_ptr->conn_owner_nlportid = info->snd_portid; + return err; } @@ -8003,6 +8007,9 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) wdev_unlock(dev->ieee80211_ptr); } + if (!err && info->attrs[NL80211_ATTR_SOCKET_OWNER]) + dev->ieee80211_ptr->conn_owner_nlportid = info->snd_portid; + return err; } @@ -8050,6 +8057,10 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info) err = cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code, local_state_change); wdev_unlock(dev->ieee80211_ptr); + + if (!err) + dev->ieee80211_ptr->conn_owner_nlportid = 0; + return err; } @@ -8097,6 +8108,10 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info) err = cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code, local_state_change); wdev_unlock(dev->ieee80211_ptr); + + if (!err) + dev->ieee80211_ptr->conn_owner_nlportid = 0; + return err; } @@ -8723,6 +8738,10 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) wdev_unlock(dev->ieee80211_ptr); if (err) kzfree(connkeys); + + if (!err && info->attrs[NL80211_ATTR_SOCKET_OWNER]) + dev->ieee80211_ptr->conn_owner_nlportid = info->snd_portid; + return err; } @@ -14425,13 +14444,21 @@ static int nl80211_netlink_notify(struct notifier_block * nb, spin_unlock(&rdev->destroy_list_lock); schedule_work(&rdev->destroy_work); } - } else if (schedule_scan_stop) { + + continue; + } + + if (schedule_scan_stop) { sched_scan_req->owner_nlportid = 0; if (rdev->ops->sched_scan_stop && rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) schedule_work(&rdev->sched_scan_stop_wk); } + + list_for_each_entry_rcu(wdev, &rdev->wiphy.wdev_list, list) + if (wdev->conn_owner_nlportid == notify->portid) + schedule_work(&wdev->disconnect_wk); } rcu_read_unlock(); diff --git a/net/wireless/sme.c b/net/wireless/sme.c index a77db33..e77f5fa 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -718,6 +718,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, cfg80211_put_bss(wdev->wiphy, bss); } cfg80211_sme_free(wdev); + wdev->conn_owner_nlportid = 0; return; } @@ -941,6 +942,7 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, wdev->current_bss = NULL; wdev->ssid_len = 0; + wdev->conn_owner_nlportid = 0; nl80211_send_disconnected(rdev, dev, reason, ie, ie_len, from_ap); @@ -1084,6 +1086,8 @@ int cfg80211_disconnect(struct cfg80211_registered_device *rdev, kzfree(wdev->connect_keys); wdev->connect_keys = NULL; + wdev->conn_owner_nlportid = 0; + if (wdev->conn) err = cfg80211_sme_disconnect(wdev, reason); else if (!rdev->ops->disconnect) -- 2.9.3