|
@@ -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, ¶ms);
|
|
|
}
|
|
|
|
|
|
+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)
|
|
|
{
|