summaryrefslogtreecommitdiff
path: root/net/wireless
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2018-03-29 23:23:26 +0300
committerDavid S. Miller <davem@davemloft.net>2018-03-29 23:23:26 +0300
commite15f20ea33b8e5074145abe464b4b48acea505d9 (patch)
treead173bb9facf7b0673a2cc313c2d52778b8f18ff /net/wireless
parente3e67a4f07433d0159dcbd2abe7d6bdf6e1268c8 (diff)
parentc470bdc1aaf36669e04ba65faf1092b2d1c6cabe (diff)
downloadlinux-e15f20ea33b8e5074145abe464b4b48acea505d9.tar.xz
Merge tag 'mac80211-next-for-davem-2018-03-29' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next
Johannes Berg says: ==================== We have a fair number of patches, but many of them are from the first bullet here: * EAPoL-over-nl80211 from Denis - this will let us fix some long-standing issues with bridging, races with encryption and more * DFS offload support from the qtnfmac folks * regulatory database changes for the new ETSI adaptivity requirements * various other fixes and small enhancements ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/wireless')
-rw-r--r--net/wireless/ap.c1
-rw-r--r--net/wireless/chan.c9
-rw-r--r--net/wireless/core.h12
-rw-r--r--net/wireless/ibss.c27
-rw-r--r--net/wireless/mesh.c16
-rw-r--r--net/wireless/mlme.c9
-rw-r--r--net/wireless/nl80211.c205
-rw-r--r--net/wireless/rdev-ops.h15
-rw-r--r--net/wireless/reg.c206
-rw-r--r--net/wireless/sme.c43
-rw-r--r--net/wireless/trace.h47
11 files changed, 513 insertions, 77 deletions
diff --git a/net/wireless/ap.c b/net/wireless/ap.c
index 63682176c96c..882d97bdc6bf 100644
--- a/net/wireless/ap.c
+++ b/net/wireless/ap.c
@@ -27,6 +27,7 @@ int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
err = rdev_stop_ap(rdev, dev);
if (!err) {
+ wdev->conn_owner_nlportid = 0;
wdev->beacon_interval = 0;
memset(&wdev->chandef, 0, sizeof(wdev->chandef));
wdev->ssid_len = 0;
diff --git a/net/wireless/chan.c b/net/wireless/chan.c
index a48859982a32..2db713d18f71 100644
--- a/net/wireless/chan.c
+++ b/net/wireless/chan.c
@@ -579,6 +579,10 @@ static bool cfg80211_get_chans_dfs_available(struct wiphy *wiphy,
{
struct ieee80211_channel *c;
u32 freq, start_freq, end_freq;
+ bool dfs_offload;
+
+ dfs_offload = wiphy_ext_feature_isset(wiphy,
+ NL80211_EXT_FEATURE_DFS_OFFLOAD);
start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
@@ -596,8 +600,9 @@ static bool cfg80211_get_chans_dfs_available(struct wiphy *wiphy,
if (c->flags & IEEE80211_CHAN_DISABLED)
return false;
- if ((c->flags & IEEE80211_CHAN_RADAR) &&
- (c->dfs_state != NL80211_DFS_AVAILABLE))
+ if ((c->flags & IEEE80211_CHAN_RADAR) &&
+ (c->dfs_state != NL80211_DFS_AVAILABLE) &&
+ !(c->dfs_state == NL80211_DFS_USABLE && dfs_offload))
return false;
}
diff --git a/net/wireless/core.h b/net/wireless/core.h
index eaff636169c2..63eb1b5fdd04 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -282,10 +282,10 @@ void cfg80211_bss_age(struct cfg80211_registered_device *rdev,
unsigned long age_secs);
/* IBSS */
-int cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
- struct net_device *dev,
- struct cfg80211_ibss_params *params,
- struct cfg80211_cached_keys *connkeys);
+int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct cfg80211_ibss_params *params,
+ struct cfg80211_cached_keys *connkeys);
void cfg80211_clear_ibss(struct net_device *dev, bool nowext);
int __cfg80211_leave_ibss(struct cfg80211_registered_device *rdev,
struct net_device *dev, bool nowext);
@@ -303,10 +303,6 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
struct net_device *dev,
struct mesh_setup *setup,
const struct mesh_config *conf);
-int cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
- struct net_device *dev,
- struct mesh_setup *setup,
- const struct mesh_config *conf);
int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev,
struct net_device *dev);
int cfg80211_leave_mesh(struct cfg80211_registered_device *rdev,
diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c
index a1d10993d08a..d1743e6abc34 100644
--- a/net/wireless/ibss.c
+++ b/net/wireless/ibss.c
@@ -84,14 +84,15 @@ void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
}
EXPORT_SYMBOL(cfg80211_ibss_joined);
-static int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
- struct net_device *dev,
- struct cfg80211_ibss_params *params,
- struct cfg80211_cached_keys *connkeys)
+int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct cfg80211_ibss_params *params,
+ struct cfg80211_cached_keys *connkeys)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
int err;
+ ASSERT_RTNL();
ASSERT_WDEV_LOCK(wdev);
if (wdev->ssid_len)
@@ -146,23 +147,6 @@ static int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
return 0;
}
-int cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
- struct net_device *dev,
- struct cfg80211_ibss_params *params,
- struct cfg80211_cached_keys *connkeys)
-{
- struct wireless_dev *wdev = dev->ieee80211_ptr;
- int err;
-
- ASSERT_RTNL();
-
- wdev_lock(wdev);
- err = __cfg80211_join_ibss(rdev, dev, params, connkeys);
- wdev_unlock(wdev);
-
- return err;
-}
-
static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
@@ -224,6 +208,7 @@ int __cfg80211_leave_ibss(struct cfg80211_registered_device *rdev,
if (err)
return err;
+ wdev->conn_owner_nlportid = 0;
__cfg80211_clear_ibss(dev, nowext);
return 0;
diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c
index b12da6ef3c12..eac5aa1419fc 100644
--- a/net/wireless/mesh.c
+++ b/net/wireless/mesh.c
@@ -217,21 +217,6 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
return err;
}
-int cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
- struct net_device *dev,
- struct mesh_setup *setup,
- const struct mesh_config *conf)
-{
- struct wireless_dev *wdev = dev->ieee80211_ptr;
- int err;
-
- wdev_lock(wdev);
- err = __cfg80211_join_mesh(rdev, dev, setup, conf);
- wdev_unlock(wdev);
-
- return err;
-}
-
int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
struct cfg80211_chan_def *chandef)
@@ -286,6 +271,7 @@ int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev,
err = rdev_leave_mesh(rdev, dev);
if (!err) {
+ wdev->conn_owner_nlportid = 0;
wdev->mesh_id_len = 0;
wdev->beacon_interval = 0;
memset(&wdev->chandef, 0, sizeof(wdev->chandef));
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index bbb9907bfa86..12b3edf70a7b 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -872,7 +872,7 @@ void cfg80211_cac_event(struct net_device *netdev,
trace_cfg80211_cac_event(netdev, event);
- if (WARN_ON(!wdev->cac_started))
+ if (WARN_ON(!wdev->cac_started && event != NL80211_RADAR_CAC_STARTED))
return;
if (WARN_ON(!wdev->chandef.chan))
@@ -888,14 +888,17 @@ void cfg80211_cac_event(struct net_device *netdev,
sizeof(struct cfg80211_chan_def));
queue_work(cfg80211_wq, &rdev->propagate_cac_done_wk);
cfg80211_sched_dfs_chan_update(rdev);
- break;
+ /* fall through */
case NL80211_RADAR_CAC_ABORTED:
+ wdev->cac_started = false;
+ break;
+ case NL80211_RADAR_CAC_STARTED:
+ wdev->cac_started = true;
break;
default:
WARN_ON(1);
return;
}
- wdev->cac_started = false;
nl80211_radar_notify(rdev, chandef, event, netdev, gfp);
}
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index a910150f8169..ff28f8feeb09 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -287,6 +287,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
[NL80211_ATTR_CONTROL_PORT] = { .type = NLA_FLAG },
[NL80211_ATTR_CONTROL_PORT_ETHERTYPE] = { .type = NLA_U16 },
[NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT] = { .type = NLA_FLAG },
+ [NL80211_ATTR_CONTROL_PORT_OVER_NL80211] = { .type = NLA_FLAG },
[NL80211_ATTR_PRIVACY] = { .type = NLA_FLAG },
[NL80211_ATTR_CIPHER_SUITE_GROUP] = { .type = NLA_U32 },
[NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 },
@@ -4134,6 +4135,9 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
wdev->chandef = params.chandef;
wdev->ssid_len = params.ssid_len;
memcpy(wdev->ssid, params.ssid, wdev->ssid_len);
+
+ if (info->attrs[NL80211_ATTR_SOCKET_OWNER])
+ wdev->conn_owner_nlportid = info->snd_portid;
}
wdev_unlock(wdev);
@@ -7551,12 +7555,13 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct net_device *dev = info->user_ptr[1];
struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct wiphy *wiphy = wdev->wiphy;
struct cfg80211_chan_def chandef;
enum nl80211_dfs_regions dfs_region;
unsigned int cac_time_ms;
int err;
- dfs_region = reg_get_dfs_region(wdev->wiphy);
+ dfs_region = reg_get_dfs_region(wiphy);
if (dfs_region == NL80211_DFS_UNSET)
return -EINVAL;
@@ -7570,17 +7575,20 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
if (wdev->cac_started)
return -EBUSY;
- err = cfg80211_chandef_dfs_required(wdev->wiphy, &chandef,
- wdev->iftype);
+ err = cfg80211_chandef_dfs_required(wiphy, &chandef, wdev->iftype);
if (err < 0)
return err;
if (err == 0)
return -EINVAL;
- if (!cfg80211_chandef_dfs_usable(wdev->wiphy, &chandef))
+ if (!cfg80211_chandef_dfs_usable(wiphy, &chandef))
return -EINVAL;
+ /* CAC start is offloaded to HW and can't be started manually */
+ if (wiphy_ext_feature_isset(wiphy, NL80211_EXT_FEATURE_DFS_OFFLOAD))
+ return -EOPNOTSUPP;
+
if (!rdev->ops->start_radar_detection)
return -EOPNOTSUPP;
@@ -8204,6 +8212,22 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
return err;
}
+static int validate_pae_over_nl80211(struct cfg80211_registered_device *rdev,
+ struct genl_info *info)
+{
+ if (!info->attrs[NL80211_ATTR_SOCKET_OWNER]) {
+ GENL_SET_ERR_MSG(info, "SOCKET_OWNER not set");
+ return -EINVAL;
+ }
+
+ if (!rdev->ops->tx_control_port ||
+ !wiphy_ext_feature_isset(&rdev->wiphy,
+ NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211))
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+
static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev,
struct genl_info *info,
struct cfg80211_crypto_settings *settings,
@@ -8227,6 +8251,15 @@ static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev,
} else
settings->control_port_ethertype = cpu_to_be16(ETH_P_PAE);
+ if (info->attrs[NL80211_ATTR_CONTROL_PORT_OVER_NL80211]) {
+ int r = validate_pae_over_nl80211(rdev, info);
+
+ if (r < 0)
+ return r;
+
+ settings->control_port_over_nl80211 = true;
+ }
+
if (info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]) {
void *data;
int len, i;
@@ -8672,12 +8705,26 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
ibss.control_port =
nla_get_flag(info->attrs[NL80211_ATTR_CONTROL_PORT]);
+ if (info->attrs[NL80211_ATTR_CONTROL_PORT_OVER_NL80211]) {
+ int r = validate_pae_over_nl80211(rdev, info);
+
+ if (r < 0)
+ return r;
+
+ ibss.control_port_over_nl80211 = true;
+ }
+
ibss.userspace_handles_dfs =
nla_get_flag(info->attrs[NL80211_ATTR_HANDLE_DFS]);
- err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys);
+ wdev_lock(dev->ieee80211_ptr);
+ err = __cfg80211_join_ibss(rdev, dev, &ibss, connkeys);
if (err)
kzfree(connkeys);
+ else if (info->attrs[NL80211_ATTR_SOCKET_OWNER])
+ dev->ieee80211_ptr->conn_owner_nlportid = info->snd_portid;
+ wdev_unlock(dev->ieee80211_ptr);
+
return err;
}
@@ -10083,7 +10130,7 @@ static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
if (err)
return err;
} else {
- /* cfg80211_join_mesh() will sort it out */
+ /* __cfg80211_join_mesh() will sort it out */
setup.chandef.chan = NULL;
}
@@ -10121,7 +10168,22 @@ static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
setup.userspace_handles_dfs =
nla_get_flag(info->attrs[NL80211_ATTR_HANDLE_DFS]);
- return cfg80211_join_mesh(rdev, dev, &setup, &cfg);
+ if (info->attrs[NL80211_ATTR_CONTROL_PORT_OVER_NL80211]) {
+ int r = validate_pae_over_nl80211(rdev, info);
+
+ if (r < 0)
+ return r;
+
+ setup.control_port_over_nl80211 = true;
+ }
+
+ wdev_lock(dev->ieee80211_ptr);
+ err = __cfg80211_join_mesh(rdev, dev, &setup, &cfg);
+ if (!err && info->attrs[NL80211_ATTR_SOCKET_OWNER])
+ dev->ieee80211_ptr->conn_owner_nlportid = info->snd_portid;
+ wdev_unlock(dev->ieee80211_ptr);
+
+ return err;
}
static int nl80211_leave_mesh(struct sk_buff *skb, struct genl_info *info)
@@ -12517,6 +12579,68 @@ static int nl80211_external_auth(struct sk_buff *skb, struct genl_info *info)
return rdev_external_auth(rdev, dev, &params);
}
+static int nl80211_tx_control_port(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ const u8 *buf;
+ size_t len;
+ u8 *dest;
+ u16 proto;
+ bool noencrypt;
+ int err;
+
+ if (!wiphy_ext_feature_isset(&rdev->wiphy,
+ NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211))
+ return -EOPNOTSUPP;
+
+ if (!rdev->ops->tx_control_port)
+ return -EOPNOTSUPP;
+
+ if (!info->attrs[NL80211_ATTR_FRAME] ||
+ !info->attrs[NL80211_ATTR_MAC] ||
+ !info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]) {
+ GENL_SET_ERR_MSG(info, "Frame, MAC or ethertype missing");
+ return -EINVAL;
+ }
+
+ wdev_lock(wdev);
+
+ switch (wdev->iftype) {
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_P2P_GO:
+ case NL80211_IFTYPE_MESH_POINT:
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ if (wdev->current_bss)
+ break;
+ err = -ENOTCONN;
+ goto out;
+ default:
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ wdev_unlock(wdev);
+
+ buf = nla_data(info->attrs[NL80211_ATTR_FRAME]);
+ len = nla_len(info->attrs[NL80211_ATTR_FRAME]);
+ dest = nla_data(info->attrs[NL80211_ATTR_MAC]);
+ proto = nla_get_u16(info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]);
+ noencrypt =
+ nla_get_flag(info->attrs[NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT]);
+
+ return rdev_tx_control_port(rdev, dev, buf, len,
+ dest, cpu_to_be16(proto), noencrypt);
+
+ out:
+ wdev_unlock(wdev);
+ return err;
+}
+
#define NL80211_FLAG_NEED_WIPHY 0x01
#define NL80211_FLAG_NEED_NETDEV 0x02
#define NL80211_FLAG_NEED_RTNL 0x04
@@ -13420,7 +13544,14 @@ static const struct genl_ops nl80211_ops[] = {
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
NL80211_FLAG_NEED_RTNL,
},
-
+ {
+ .cmd = NL80211_CMD_CONTROL_PORT_FRAME,
+ .doit = nl80211_tx_control_port,
+ .policy = nl80211_policy,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
};
static struct genl_family nl80211_fam __ro_after_init = {
@@ -14535,6 +14666,64 @@ void cfg80211_mgmt_tx_status(struct wireless_dev *wdev, u64 cookie,
}
EXPORT_SYMBOL(cfg80211_mgmt_tx_status);
+static int __nl80211_rx_control_port(struct net_device *dev,
+ const u8 *buf, size_t len,
+ const u8 *addr, u16 proto,
+ bool unencrypted, gfp_t gfp)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ struct sk_buff *msg;
+ void *hdr;
+ u32 nlportid = READ_ONCE(wdev->conn_owner_nlportid);
+
+ if (!nlportid)
+ return -ENOENT;
+
+ msg = nlmsg_new(100 + len, gfp);
+ if (!msg)
+ return -ENOMEM;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CONTROL_PORT_FRAME);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+ nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
+ nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev),
+ NL80211_ATTR_PAD) ||
+ nla_put(msg, NL80211_ATTR_FRAME, len, buf) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
+ nla_put_u16(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE, proto) ||
+ (unencrypted && nla_put_flag(msg,
+ NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT)))
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+
+ return genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlportid);
+
+ nla_put_failure:
+ nlmsg_free(msg);
+ return -ENOBUFS;
+}
+
+bool cfg80211_rx_control_port(struct net_device *dev,
+ const u8 *buf, size_t len,
+ const u8 *addr, u16 proto, bool unencrypted)
+{
+ int ret;
+
+ trace_cfg80211_rx_control_port(dev, buf, len, addr, proto, unencrypted);
+ ret = __nl80211_rx_control_port(dev, buf, len, addr, proto,
+ unencrypted, GFP_ATOMIC);
+ trace_cfg80211_return_bool(ret == 0);
+ return ret == 0;
+}
+EXPORT_SYMBOL(cfg80211_rx_control_port);
+
static struct sk_buff *cfg80211_prepare_cqm(struct net_device *dev,
const char *mac, gfp_t gfp)
{
diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
index 84f23ae015fc..87479a53411b 100644
--- a/net/wireless/rdev-ops.h
+++ b/net/wireless/rdev-ops.h
@@ -714,6 +714,21 @@ static inline int rdev_mgmt_tx(struct cfg80211_registered_device *rdev,
return ret;
}
+static inline int rdev_tx_control_port(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ const void *buf, size_t len,
+ const u8 *dest, __be16 proto,
+ const bool noencrypt)
+{
+ int ret;
+ trace_rdev_tx_control_port(&rdev->wiphy, dev, buf, len,
+ dest, proto, noencrypt);
+ ret = rdev->ops->tx_control_port(&rdev->wiphy, dev, buf, len,
+ dest, proto, noencrypt);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
static inline int
rdev_mgmt_tx_cancel_wait(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev, u64 cookie)
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 7b42f0bacfd8..16c7e4ef5820 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -5,6 +5,7 @@
* Copyright 2008-2011 Luis R. Rodriguez <mcgrof@qca.qualcomm.com>
* Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright 2017 Intel Deutschland GmbH
+ * Copyright (C) 2018 Intel Corporation
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -134,12 +135,12 @@ static void restore_regulatory_settings(bool reset_user);
static const struct ieee80211_regdomain *get_cfg80211_regdom(void)
{
- return rtnl_dereference(cfg80211_regdomain);
+ return rcu_dereference_rtnl(cfg80211_regdomain);
}
const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy)
{
- return rtnl_dereference(wiphy->regd);
+ return rcu_dereference_rtnl(wiphy->regd);
}
static const char *reg_dfs_region_str(enum nl80211_dfs_regions dfs_region)
@@ -424,23 +425,36 @@ static const struct ieee80211_regdomain *
reg_copy_regd(const struct ieee80211_regdomain *src_regd)
{
struct ieee80211_regdomain *regd;
- int size_of_regd;
+ int size_of_regd, size_of_wmms;
unsigned int i;
+ struct ieee80211_wmm_rule *d_wmm, *s_wmm;
size_of_regd =
sizeof(struct ieee80211_regdomain) +
src_regd->n_reg_rules * sizeof(struct ieee80211_reg_rule);
+ size_of_wmms = src_regd->n_wmm_rules *
+ sizeof(struct ieee80211_wmm_rule);
- regd = kzalloc(size_of_regd, GFP_KERNEL);
+ regd = kzalloc(size_of_regd + size_of_wmms, GFP_KERNEL);
if (!regd)
return ERR_PTR(-ENOMEM);
memcpy(regd, src_regd, sizeof(struct ieee80211_regdomain));
- for (i = 0; i < src_regd->n_reg_rules; i++)
+ d_wmm = (struct ieee80211_wmm_rule *)((u8 *)regd + size_of_regd);
+ s_wmm = (struct ieee80211_wmm_rule *)((u8 *)src_regd + size_of_regd);
+ memcpy(d_wmm, s_wmm, size_of_wmms);
+
+ for (i = 0; i < src_regd->n_reg_rules; i++) {
memcpy(&regd->reg_rules[i], &src_regd->reg_rules[i],
sizeof(struct ieee80211_reg_rule));
+ if (!src_regd->reg_rules[i].wmm_rule)
+ continue;
+ regd->reg_rules[i].wmm_rule = d_wmm +
+ (src_regd->reg_rules[i].wmm_rule - s_wmm) /
+ sizeof(struct ieee80211_wmm_rule);
+ }
return regd;
}
@@ -595,6 +609,17 @@ enum fwdb_flags {
FWDB_FLAG_AUTO_BW = BIT(4),
};
+struct fwdb_wmm_ac {
+ u8 ecw;
+ u8 aifsn;
+ __be16 cot;
+} __packed;
+
+struct fwdb_wmm_rule {
+ struct fwdb_wmm_ac client[IEEE80211_NUM_ACS];
+ struct fwdb_wmm_ac ap[IEEE80211_NUM_ACS];
+} __packed;
+
struct fwdb_rule {
u8 len;
u8 flags;
@@ -602,6 +627,7 @@ struct fwdb_rule {
__be32 start, end, max_bw;
/* start of optional data */
__be16 cac_timeout;
+ __be16 wmm_ptr;
} __packed __aligned(4);
#define FWDB_MAGIC 0x52474442
@@ -613,6 +639,31 @@ struct fwdb_header {
struct fwdb_country country[];
} __packed __aligned(4);
+static int ecw2cw(int ecw)
+{
+ return (1 << ecw) - 1;
+}
+
+static bool valid_wmm(struct fwdb_wmm_rule *rule)
+{
+ struct fwdb_wmm_ac *ac = (struct fwdb_wmm_ac *)rule;
+ int i;
+
+ for (i = 0; i < IEEE80211_NUM_ACS * 2; i++) {
+ u16 cw_min = ecw2cw((ac[i].ecw & 0xf0) >> 4);
+ u16 cw_max = ecw2cw(ac[i].ecw & 0x0f);
+ u8 aifsn = ac[i].aifsn;
+
+ if (cw_min >= cw_max)
+ return false;
+
+ if (aifsn < 1)
+ return false;
+ }
+
+ return true;
+}
+
static bool valid_rule(const u8 *data, unsigned int size, u16 rule_ptr)
{
struct fwdb_rule *rule = (void *)(data + (rule_ptr << 2));
@@ -623,7 +674,18 @@ static bool valid_rule(const u8 *data, unsigned int size, u16 rule_ptr)
/* mandatory fields */
if (rule->len < offsetofend(struct fwdb_rule, max_bw))
return false;
+ if (rule->len >= offsetofend(struct fwdb_rule, wmm_ptr)) {
+ u32 wmm_ptr = be16_to_cpu(rule->wmm_ptr) << 2;
+ struct fwdb_wmm_rule *wmm;
+
+ if (wmm_ptr + sizeof(struct fwdb_wmm_rule) > size)
+ return false;
+ wmm = (void *)(data + wmm_ptr);
+
+ if (!valid_wmm(wmm))
+ return false;
+ }
return true;
}
@@ -798,23 +860,118 @@ static bool valid_regdb(const u8 *data, unsigned int size)
return true;
}
+static void set_wmm_rule(struct ieee80211_wmm_rule *rule,
+ struct fwdb_wmm_rule *wmm)
+{
+ unsigned int i;
+
+ for (i = 0; i < IEEE80211_NUM_ACS; i++) {
+ rule->client[i].cw_min =
+ ecw2cw((wmm->client[i].ecw & 0xf0) >> 4);
+ rule->client[i].cw_max = ecw2cw(wmm->client[i].ecw & 0x0f);
+ rule->client[i].aifsn = wmm->client[i].aifsn;
+ rule->client[i].cot = 1000 * be16_to_cpu(wmm->client[i].cot);
+ rule->ap[i].cw_min = ecw2cw((wmm->ap[i].ecw & 0xf0) >> 4);
+ rule->ap[i].cw_max = ecw2cw(wmm->ap[i].ecw & 0x0f);
+ rule->ap[i].aifsn = wmm->ap[i].aifsn;
+ rule->ap[i].cot = 1000 * be16_to_cpu(wmm->ap[i].cot);
+ }
+}
+
+static int __regdb_query_wmm(const struct fwdb_header *db,
+ const struct fwdb_country *country, int freq,
+ u32 *dbptr, struct ieee80211_wmm_rule *rule)
+{
+ unsigned int ptr = be16_to_cpu(country->coll_ptr) << 2;
+ struct fwdb_collection *coll = (void *)((u8 *)db + ptr);
+ int i;
+
+ for (i = 0; i < coll->n_rules; i++) {
+ __be16 *rules_ptr = (void *)((u8 *)coll + ALIGN(coll->len, 2));
+ unsigned int rule_ptr = be16_to_cpu(rules_ptr[i]) << 2;
+ struct fwdb_rule *rrule = (void *)((u8 *)db + rule_ptr);
+ struct fwdb_wmm_rule *wmm;
+ unsigned int wmm_ptr;
+
+ if (rrule->len < offsetofend(struct fwdb_rule, wmm_ptr))
+ continue;
+
+ if (freq >= KHZ_TO_MHZ(be32_to_cpu(rrule->start)) &&
+ freq <= KHZ_TO_MHZ(be32_to_cpu(rrule->end))) {
+ wmm_ptr = be16_to_cpu(rrule->wmm_ptr) << 2;
+ wmm = (void *)((u8 *)db + wmm_ptr);
+ set_wmm_rule(rule, wmm);
+ if (dbptr)
+ *dbptr = wmm_ptr;
+ return 0;
+ }
+ }
+
+ return -ENODATA;
+}
+
+int reg_query_regdb_wmm(char *alpha2, int freq, u32 *dbptr,
+ struct ieee80211_wmm_rule *rule)
+{
+ const struct fwdb_header *hdr = regdb;
+ const struct fwdb_country *country;
+
+ if (IS_ERR(regdb))
+ return PTR_ERR(regdb);
+
+ country = &hdr->country[0];
+ while (country->coll_ptr) {
+ if (alpha2_equal(alpha2, country->alpha2))
+ return __regdb_query_wmm(regdb, country, freq, dbptr,
+ rule);
+
+ country++;
+ }
+
+ return -ENODATA;
+}
+EXPORT_SYMBOL(reg_query_regdb_wmm);
+
+struct wmm_ptrs {
+ struct ieee80211_wmm_rule *rule;
+ u32 ptr;
+};
+
+static struct ieee80211_wmm_rule *find_wmm_ptr(struct wmm_ptrs *wmm_ptrs,
+ u32 wmm_ptr, int n_wmms)
+{
+ int i;
+
+ for (i = 0; i < n_wmms; i++) {
+ if (wmm_ptrs[i].ptr == wmm_ptr)
+ return wmm_ptrs[i].rule;
+ }
+ return NULL;
+}
+
static int regdb_query_country(const struct fwdb_header *db,
const struct fwdb_country *country)
{
unsigned int ptr = be16_to_cpu(country->coll_ptr) << 2;
struct fwdb_collection *coll = (void *)((u8 *)db + ptr);
struct ieee80211_regdomain *regdom;
- unsigned int size_of_regd;
- unsigned int i;
+ struct ieee80211_regdomain *tmp_rd;
+ unsigned int size_of_regd, i, n_wmms = 0;
+ struct wmm_ptrs *wmm_ptrs;
- size_of_regd =
- sizeof(struct ieee80211_regdomain) +
+ size_of_regd = sizeof(struct ieee80211_regdomain) +
coll->n_rules * sizeof(struct ieee80211_reg_rule);
regdom = kzalloc(size_of_regd, GFP_KERNEL);
if (!regdom)
return -ENOMEM;
+ wmm_ptrs = kcalloc(coll->n_rules, sizeof(*wmm_ptrs), GFP_KERNEL);
+ if (!wmm_ptrs) {
+ kfree(regdom);
+ return -ENOMEM;
+ }
+
regdom->n_reg_rules = coll->n_rules;
regdom->alpha2[0] = country->alpha2[0];
regdom->alpha2[1] = country->alpha2[1];
@@ -851,7 +1008,38 @@ static int regdb_query_country(const struct fwdb_header *db,
if (rule->len >= offsetofend(struct fwdb_rule, cac_timeout))
rrule->dfs_cac_ms =
1000 * be16_to_cpu(rule->cac_timeout);
+ if (rule->len >= offsetofend(struct fwdb_rule, wmm_ptr)) {
+ u32 wmm_ptr = be16_to_cpu(rule->wmm_ptr) << 2;
+ struct ieee80211_wmm_rule *wmm_pos =
+ find_wmm_ptr(wmm_ptrs, wmm_ptr, n_wmms);
+ struct fwdb_wmm_rule *wmm;
+ struct ieee80211_wmm_rule *wmm_rule;
+
+ if (wmm_pos) {
+ rrule->wmm_rule = wmm_pos;
+ continue;
+ }
+ wmm = (void *)((u8 *)db + wmm_ptr);
+ tmp_rd = krealloc(regdom, size_of_regd + (n_wmms + 1) *
+ sizeof(struct ieee80211_wmm_rule),
+ GFP_KERNEL);
+
+ if (!tmp_rd) {
+ kfree(regdom);
+ return -ENOMEM;
+ }
+ regdom = tmp_rd;
+
+ wmm_rule = (struct ieee80211_wmm_rule *)
+ ((u8 *)regdom + size_of_regd + n_wmms *
+ sizeof(struct ieee80211_wmm_rule));
+
+ set_wmm_rule(wmm_rule, wmm);
+ wmm_ptrs[n_wmms].ptr = wmm_ptr;
+ wmm_ptrs[n_wmms++].rule = wmm_rule;
+ }
}
+ kfree(wmm_ptrs);
return reg_schedule_apply(regdom);
}
diff --git a/net/wireless/sme.c b/net/wireless/sme.c
index 701cfd7acc1b..5df6b33db786 100644
--- a/net/wireless/sme.c
+++ b/net/wireless/sme.c
@@ -1239,17 +1239,38 @@ void cfg80211_autodisconnect_wk(struct work_struct *work)
wdev_lock(wdev);
if (wdev->conn_owner_nlportid) {
- /*
- * Use disconnect_bssid if still connecting and ops->disconnect
- * not implemented. Otherwise we can use cfg80211_disconnect.
- */
- if (rdev->ops->disconnect || wdev->current_bss)
- cfg80211_disconnect(rdev, wdev->netdev,
- WLAN_REASON_DEAUTH_LEAVING, true);
- else
- cfg80211_mlme_deauth(rdev, wdev->netdev,
- wdev->disconnect_bssid, NULL, 0,
- WLAN_REASON_DEAUTH_LEAVING, false);
+ switch (wdev->iftype) {
+ case NL80211_IFTYPE_ADHOC:
+ cfg80211_leave_ibss(rdev, wdev->netdev, false);
+ break;
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_P2P_GO:
+ cfg80211_stop_ap(rdev, wdev->netdev, false);
+ break;
+ case NL80211_IFTYPE_MESH_POINT:
+ cfg80211_leave_mesh(rdev, wdev->netdev);
+ break;
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ /*
+ * Use disconnect_bssid if still connecting and
+ * ops->disconnect not implemented. Otherwise we can
+ * use cfg80211_disconnect.
+ */
+ if (rdev->ops->disconnect || wdev->current_bss)
+ cfg80211_disconnect(rdev, wdev->netdev,
+ WLAN_REASON_DEAUTH_LEAVING,
+ true);
+ else
+ cfg80211_mlme_deauth(rdev, wdev->netdev,
+ wdev->disconnect_bssid,
+ NULL, 0,
+ WLAN_REASON_DEAUTH_LEAVING,
+ false);
+ break;
+ default:
+ break;
+ }
}
wdev_unlock(wdev);
diff --git a/net/wireless/trace.h b/net/wireless/trace.h
index 5152938b358d..a64291ae52a6 100644
--- a/net/wireless/trace.h
+++ b/net/wireless/trace.h
@@ -1882,6 +1882,32 @@ TRACE_EVENT(rdev_mgmt_tx,
BOOL_TO_STR(__entry->dont_wait_for_ack))
);
+TRACE_EVENT(rdev_tx_control_port,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ const u8 *buf, size_t len, const u8 *dest, __be16 proto,
+ bool unencrypted),
+ TP_ARGS(wiphy, netdev, buf, len, dest, proto, unencrypted),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(dest)
+ __field(__be16, proto)
+ __field(bool, unencrypted)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(dest, dest);
+ __entry->proto = proto;
+ __entry->unencrypted = unencrypted;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " MAC_PR_FMT ","
+ " proto: 0x%x, unencrypted: %s",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(dest),
+ be16_to_cpu(__entry->proto),
+ BOOL_TO_STR(__entry->unencrypted))
+);
+
TRACE_EVENT(rdev_set_noack_map,
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
u16 noack_map),
@@ -2600,6 +2626,27 @@ TRACE_EVENT(cfg80211_mgmt_tx_status,
WDEV_PR_ARG, __entry->cookie, BOOL_TO_STR(__entry->ack))
);
+TRACE_EVENT(cfg80211_rx_control_port,
+ TP_PROTO(struct net_device *netdev, const u8 *buf, size_t len,
+ const u8 *addr, u16 proto, bool unencrypted),
+ TP_ARGS(netdev, buf, len, addr, proto, unencrypted),
+ TP_STRUCT__entry(
+ NETDEV_ENTRY
+ MAC_ENTRY(addr)
+ __field(u16, proto)
+ __field(bool, unencrypted)
+ ),
+ TP_fast_assign(
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(addr, addr);
+ __entry->proto = proto;
+ __entry->unencrypted = unencrypted;
+ ),
+ TP_printk(NETDEV_PR_FMT ", " MAC_PR_FMT " proto: 0x%x, unencrypted: %s",
+ NETDEV_PR_ARG, MAC_PR_ARG(addr),
+ __entry->proto, BOOL_TO_STR(__entry->unencrypted))
+);
+
TRACE_EVENT(cfg80211_cqm_rssi_notify,
TP_PROTO(struct net_device *netdev,
enum nl80211_cqm_rssi_threshold_event rssi_event,