|
@@ -761,6 +761,8 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
|
|
}
|
|
}
|
|
CMD(set_channel, SET_CHANNEL);
|
|
CMD(set_channel, SET_CHANNEL);
|
|
CMD(set_wds_peer, SET_WDS_PEER);
|
|
CMD(set_wds_peer, SET_WDS_PEER);
|
|
|
|
+ if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN)
|
|
|
|
+ CMD(sched_scan_start, START_SCHED_SCAN);
|
|
|
|
|
|
#undef CMD
|
|
#undef CMD
|
|
|
|
|
|
@@ -3357,6 +3359,179 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
|
|
return err;
|
|
return err;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int nl80211_start_sched_scan(struct sk_buff *skb,
|
|
|
|
+ struct genl_info *info)
|
|
|
|
+{
|
|
|
|
+ struct cfg80211_sched_scan_request *request;
|
|
|
|
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
+ struct net_device *dev = info->user_ptr[1];
|
|
|
|
+ struct cfg80211_ssid *ssid;
|
|
|
|
+ struct ieee80211_channel *channel;
|
|
|
|
+ struct nlattr *attr;
|
|
|
|
+ struct wiphy *wiphy;
|
|
|
|
+ int err, tmp, n_ssids = 0, n_channels, i;
|
|
|
|
+ enum ieee80211_band band;
|
|
|
|
+ size_t ie_len;
|
|
|
|
+
|
|
|
|
+ if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
|
|
|
|
+ !rdev->ops->sched_scan_start)
|
|
|
|
+ return -EOPNOTSUPP;
|
|
|
|
+
|
|
|
|
+ if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ if (rdev->sched_scan_req)
|
|
|
|
+ return -EINPROGRESS;
|
|
|
|
+
|
|
|
|
+ wiphy = &rdev->wiphy;
|
|
|
|
+
|
|
|
|
+ if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
|
|
|
|
+ n_channels = validate_scan_freqs(
|
|
|
|
+ info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
|
|
|
|
+ if (!n_channels)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ } else {
|
|
|
|
+ n_channels = 0;
|
|
|
|
+
|
|
|
|
+ for (band = 0; band < IEEE80211_NUM_BANDS; band++)
|
|
|
|
+ if (wiphy->bands[band])
|
|
|
|
+ n_channels += wiphy->bands[band]->n_channels;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
|
|
|
|
+ nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS],
|
|
|
|
+ tmp)
|
|
|
|
+ n_ssids++;
|
|
|
|
+
|
|
|
|
+ if (n_ssids > wiphy->max_scan_ssids)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ if (info->attrs[NL80211_ATTR_IE])
|
|
|
|
+ ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
|
|
|
|
+ else
|
|
|
|
+ ie_len = 0;
|
|
|
|
+
|
|
|
|
+ if (ie_len > wiphy->max_scan_ie_len)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ request = kzalloc(sizeof(*request)
|
|
|
|
+ + sizeof(*ssid) * n_ssids
|
|
|
|
+ + sizeof(channel) * n_channels
|
|
|
|
+ + ie_len, GFP_KERNEL);
|
|
|
|
+ if (!request)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+
|
|
|
|
+ if (n_ssids)
|
|
|
|
+ request->ssids = (void *)&request->channels[n_channels];
|
|
|
|
+ request->n_ssids = n_ssids;
|
|
|
|
+ if (ie_len) {
|
|
|
|
+ if (request->ssids)
|
|
|
|
+ request->ie = (void *)(request->ssids + n_ssids);
|
|
|
|
+ else
|
|
|
|
+ request->ie = (void *)(request->channels + n_channels);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ i = 0;
|
|
|
|
+ if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
|
|
|
|
+ /* user specified, bail out if channel not found */
|
|
|
|
+ nla_for_each_nested(attr,
|
|
|
|
+ info->attrs[NL80211_ATTR_SCAN_FREQUENCIES],
|
|
|
|
+ tmp) {
|
|
|
|
+ struct ieee80211_channel *chan;
|
|
|
|
+
|
|
|
|
+ chan = ieee80211_get_channel(wiphy, nla_get_u32(attr));
|
|
|
|
+
|
|
|
|
+ if (!chan) {
|
|
|
|
+ err = -EINVAL;
|
|
|
|
+ goto out_free;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* ignore disabled channels */
|
|
|
|
+ if (chan->flags & IEEE80211_CHAN_DISABLED)
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ request->channels[i] = chan;
|
|
|
|
+ i++;
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ /* all channels */
|
|
|
|
+ for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
|
|
|
|
+ int j;
|
|
|
|
+ if (!wiphy->bands[band])
|
|
|
|
+ continue;
|
|
|
|
+ for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
|
|
|
|
+ struct ieee80211_channel *chan;
|
|
|
|
+
|
|
|
|
+ chan = &wiphy->bands[band]->channels[j];
|
|
|
|
+
|
|
|
|
+ if (chan->flags & IEEE80211_CHAN_DISABLED)
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ request->channels[i] = chan;
|
|
|
|
+ i++;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!i) {
|
|
|
|
+ err = -EINVAL;
|
|
|
|
+ goto out_free;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ request->n_channels = i;
|
|
|
|
+
|
|
|
|
+ i = 0;
|
|
|
|
+ if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
|
|
|
|
+ nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS],
|
|
|
|
+ tmp) {
|
|
|
|
+ if (request->ssids[i].ssid_len >
|
|
|
|
+ IEEE80211_MAX_SSID_LEN) {
|
|
|
|
+ err = -EINVAL;
|
|
|
|
+ goto out_free;
|
|
|
|
+ }
|
|
|
|
+ memcpy(request->ssids[i].ssid, nla_data(attr),
|
|
|
|
+ nla_len(attr));
|
|
|
|
+ request->ssids[i].ssid_len = nla_len(attr);
|
|
|
|
+ i++;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (info->attrs[NL80211_ATTR_IE]) {
|
|
|
|
+ request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
|
|
|
|
+ memcpy((void *)request->ie,
|
|
|
|
+ nla_data(info->attrs[NL80211_ATTR_IE]),
|
|
|
|
+ request->ie_len);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ request->dev = dev;
|
|
|
|
+ request->wiphy = &rdev->wiphy;
|
|
|
|
+
|
|
|
|
+ err = rdev->ops->sched_scan_start(&rdev->wiphy, dev, request);
|
|
|
|
+ if (!err) {
|
|
|
|
+ rdev->sched_scan_req = request;
|
|
|
|
+ nl80211_send_sched_scan(rdev, dev,
|
|
|
|
+ NL80211_CMD_START_SCHED_SCAN);
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+out_free:
|
|
|
|
+ kfree(request);
|
|
|
|
+out:
|
|
|
|
+ return err;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int nl80211_stop_sched_scan(struct sk_buff *skb,
|
|
|
|
+ struct genl_info *info)
|
|
|
|
+{
|
|
|
|
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
+
|
|
|
|
+ if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
|
|
|
|
+ !rdev->ops->sched_scan_stop)
|
|
|
|
+ return -EOPNOTSUPP;
|
|
|
|
+
|
|
|
|
+ return __cfg80211_stop_sched_scan(rdev, false);
|
|
|
|
+}
|
|
|
|
+
|
|
static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags,
|
|
static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags,
|
|
struct cfg80211_registered_device *rdev,
|
|
struct cfg80211_registered_device *rdev,
|
|
struct wireless_dev *wdev,
|
|
struct wireless_dev *wdev,
|
|
@@ -5326,6 +5501,22 @@ static struct genl_ops nl80211_ops[] = {
|
|
.policy = nl80211_policy,
|
|
.policy = nl80211_policy,
|
|
.dumpit = nl80211_dump_scan,
|
|
.dumpit = nl80211_dump_scan,
|
|
},
|
|
},
|
|
|
|
+ {
|
|
|
|
+ .cmd = NL80211_CMD_START_SCHED_SCAN,
|
|
|
|
+ .doit = nl80211_start_sched_scan,
|
|
|
|
+ .policy = nl80211_policy,
|
|
|
|
+ .flags = GENL_ADMIN_PERM,
|
|
|
|
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
|
|
|
+ NL80211_FLAG_NEED_RTNL,
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ .cmd = NL80211_CMD_STOP_SCHED_SCAN,
|
|
|
|
+ .doit = nl80211_stop_sched_scan,
|
|
|
|
+ .policy = nl80211_policy,
|
|
|
|
+ .flags = GENL_ADMIN_PERM,
|
|
|
|
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
|
|
|
+ NL80211_FLAG_NEED_RTNL,
|
|
|
|
+ },
|
|
{
|
|
{
|
|
.cmd = NL80211_CMD_AUTHENTICATE,
|
|
.cmd = NL80211_CMD_AUTHENTICATE,
|
|
.doit = nl80211_authenticate,
|
|
.doit = nl80211_authenticate,
|
|
@@ -5652,6 +5843,28 @@ static int nl80211_send_scan_msg(struct sk_buff *msg,
|
|
return -EMSGSIZE;
|
|
return -EMSGSIZE;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int
|
|
|
|
+nl80211_send_sched_scan_msg(struct sk_buff *msg,
|
|
|
|
+ struct cfg80211_registered_device *rdev,
|
|
|
|
+ struct net_device *netdev,
|
|
|
|
+ u32 pid, u32 seq, int flags, u32 cmd)
|
|
|
|
+{
|
|
|
|
+ void *hdr;
|
|
|
|
+
|
|
|
|
+ hdr = nl80211hdr_put(msg, pid, seq, flags, cmd);
|
|
|
|
+ if (!hdr)
|
|
|
|
+ return -1;
|
|
|
|
+
|
|
|
|
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
|
|
|
|
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
|
|
|
|
+
|
|
|
|
+ return genlmsg_end(msg, hdr);
|
|
|
|
+
|
|
|
|
+ nla_put_failure:
|
|
|
|
+ genlmsg_cancel(msg, hdr);
|
|
|
|
+ return -EMSGSIZE;
|
|
|
|
+}
|
|
|
|
+
|
|
void nl80211_send_scan_start(struct cfg80211_registered_device *rdev,
|
|
void nl80211_send_scan_start(struct cfg80211_registered_device *rdev,
|
|
struct net_device *netdev)
|
|
struct net_device *netdev)
|
|
{
|
|
{
|
|
@@ -5709,6 +5922,43 @@ void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
|
|
nl80211_scan_mcgrp.id, GFP_KERNEL);
|
|
nl80211_scan_mcgrp.id, GFP_KERNEL);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev,
|
|
|
|
+ struct net_device *netdev)
|
|
|
|
+{
|
|
|
|
+ struct sk_buff *msg;
|
|
|
|
+
|
|
|
|
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
|
|
|
+ if (!msg)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0,
|
|
|
|
+ NL80211_CMD_SCHED_SCAN_RESULTS) < 0) {
|
|
|
|
+ nlmsg_free(msg);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
|
|
|
|
+ nl80211_scan_mcgrp.id, GFP_KERNEL);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev,
|
|
|
|
+ struct net_device *netdev, u32 cmd)
|
|
|
|
+{
|
|
|
|
+ struct sk_buff *msg;
|
|
|
|
+
|
|
|
|
+ msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
|
|
|
|
+ if (!msg)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0, cmd) < 0) {
|
|
|
|
+ nlmsg_free(msg);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
|
|
|
|
+ nl80211_scan_mcgrp.id, GFP_KERNEL);
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* This can happen on global regulatory changes or device specific settings
|
|
* This can happen on global regulatory changes or device specific settings
|
|
* based on custom world regulatory domains.
|
|
* based on custom world regulatory domains.
|