Search Linux Wireless

[PATCH] nl80211/cfg80211: add ssid filtering for sched_scan

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Introduce filtering for scheduled scans to reduce the number of
unnecessary results (which cause useless wake-ups).

This commit adds a new nested attribute for passing generic filters in
scheduled scan requests.  It also adds an implementation for filtering
based on a list of SSIDs.  In the future, more types of filters will
be added.

Signed-off-by: Luciano Coelho <coelho@xxxxxx>
---
 include/linux/nl80211.h |   33 ++++++++++++++++++++++++++++++++
 include/net/cfg80211.h  |    8 +++++++
 net/wireless/nl80211.c  |   48 +++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 87 insertions(+), 2 deletions(-)

diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h
index 3769303..7d4edb8 100644
--- a/include/linux/nl80211.h
+++ b/include/linux/nl80211.h
@@ -769,6 +769,8 @@ enum nl80211_commands {
  *	that can be added to a scan request
  * @NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN: maximum length of information
  *	elements that can be added to a scheduled scan request
+ * @NL80211_ATTR_MAX_FILTER_SSIDS: number of SSIDs you can use as
+ *	filters in a scheduled scan request, a wiphy attribute.
  *
  * @NL80211_ATTR_SCAN_FREQUENCIES: nested attribute with frequencies (in MHz)
  * @NL80211_ATTR_SCAN_SSIDS: nested attribute with SSIDs, leave out for passive
@@ -1011,6 +1013,13 @@ enum nl80211_commands {
 
  * @NL80211_ATTR_SCHED_SCAN_INTERVAL: Interval between scheduled scan
  *	cycles, in msecs.
+ * @NL80211_ATTR_SCHED_SCAN_FILTER: Nested attribute with various
+ *	types of filtering to be used with scheduled scans.
+ *	If @NL80211_ATTR_SCAN_SSIDS is passed with values that are not
+ *	included in the filter, the driver may return -EINVAL, since
+ *	it doesn't make sense to send probe requests with SSIDs that
+ *	will be filtered out.  This doesn't apply to the wildcard SSID.
+ *	If ommited, no filtering is done.
  *
  * @NL80211_ATTR_INTERFACE_COMBINATIONS: Nested attribute listing the supported
  *	interface combinations. In each nested item, it contains attributes
@@ -1252,6 +1261,9 @@ enum nl80211_attrs {
 	NL80211_ATTR_IE_PROBE_RESP,
 	NL80211_ATTR_IE_ASSOC_RESP,
 
+	NL80211_ATTR_SCHED_SCAN_FILTER,
+	NL80211_ATTR_MAX_FILTER_SSIDS,
+
 	/* add attributes here, update the policy in nl80211.c */
 
 	__NL80211_ATTR_AFTER_LAST,
@@ -1711,6 +1723,27 @@ enum nl80211_reg_rule_attr {
 };
 
 /**
+ * enum nl80211_sched_scan_filter_attr - scheduled scan filter attributes
+ * @__NL80211_SCHED_SCAN_FILTER_ATTR_INVALID: attribute number 0 is reserved
+ * @NL80211_SCHED_SCAN_FILTER_SSID: A list of SSIDs to be automatically
+ *	filtered by the hardware and passed up to the host.  SSIDs that are
+ *	not present in this list are ignored (without waking up the host).
+ * @NL80211_SCHED_SCAN_FILTER_ATTR_MAX: highest scheduled scan filter
+ *	attribute number currently defined
+ * @__NL80211_SCHED_SCAN_FILTER_ATTR_AFTER_LAST: internal use
+ */
+enum nl80211_sched_scan_filter_attr {
+	__NL80211_SCHED_SCAN_FILTER_ATTR_INVALID,
+
+	NL80211_ATTR_SCHED_SCAN_FILTER_SSID,
+
+	/* keep last */
+	__NL80211_SCHED_SCAN_FILTER_ATTR_AFTER_LAST,
+	NL80211_SCHED_SCAN_FILTER_ATTR_MAX =
+		__NL80211_SCHED_SCAN_FILTER_ATTR_AFTER_LAST - 1
+};
+
+/**
  * enum nl80211_reg_rule_flags - regulatory rule flags
  *
  * @NL80211_RRF_NO_OFDM: OFDM modulation not allowed
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index a37f264..a3e58ff 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -882,6 +882,9 @@ struct cfg80211_scan_request {
  * @interval: interval between each scheduled scan cycle
  * @ie: optional information element(s) to add into Probe Request or %NULL
  * @ie_len: length of ie in octets
+ * @filter_ssids: SSIDs to pass to the host (others are filtered out).
+ * 	If ommited, no filtering is done.
+ * @n_filter_ssids: number of filter SSIDs
  * @wiphy: the wiphy this was for
  * @dev: the interface
  * @channels: channels to scan
@@ -893,6 +896,8 @@ struct cfg80211_sched_scan_request {
 	u32 interval;
 	const u8 *ie;
 	size_t ie_len;
+	struct cfg80211_ssid *filter_ssids;
+	int n_filter_ssids;
 
 	/* internal */
 	struct wiphy *wiphy;
@@ -1806,6 +1811,8 @@ struct wiphy_wowlan_support {
  *	any given scan
  * @max_sched_scan_ssids: maximum number of SSIDs the device can scan
  *	for in any given scheduled scan
+ * @max_filter_ssids: maximum number of SSIDs the device can filter when
+ *	performing a scheduled scan, 0 if filtering is not supported
  * @max_scan_ie_len: maximum length of user-controlled IEs device can
  *	add to probe request frames transmitted during a scan, must not
  *	include fixed IEs like supported rates
@@ -1863,6 +1870,7 @@ struct wiphy {
 	int bss_priv_size;
 	u8 max_scan_ssids;
 	u8 max_sched_scan_ssids;
+	u8 max_filter_ssids;
 	u16 max_scan_ie_len;
 	u16 max_sched_scan_ie_len;
 
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 57ecfa4..cd46a07 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -189,6 +189,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
 					 .len = IEEE80211_MAX_DATA_LEN },
 	[NL80211_ATTR_IE_ASSOC_RESP] = { .type = NLA_BINARY,
 					 .len = IEEE80211_MAX_DATA_LEN },
+	[NL80211_ATTR_SCHED_SCAN_FILTER] = { .type = NLA_NESTED },
 };
 
 /* policy for the key attributes */
@@ -714,6 +715,8 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
 		    dev->wiphy.max_scan_ie_len);
 	NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN,
 		    dev->wiphy.max_sched_scan_ie_len);
+	NLA_PUT_U8(msg, NL80211_ATTR_MAX_FILTER_SSIDS,
+		   dev->wiphy.max_filter_ssids);
 
 	if (dev->wiphy.flags & WIPHY_FLAG_IBSS_RSN)
 		NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_IBSS_RSN);
@@ -3596,9 +3599,9 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
 	struct cfg80211_sched_scan_request *request;
 	struct cfg80211_registered_device *rdev = info->user_ptr[0];
 	struct net_device *dev = info->user_ptr[1];
-	struct nlattr *attr;
+	struct nlattr *attr, *attr2;
 	struct wiphy *wiphy;
-	int err, tmp, n_ssids = 0, n_channels, i;
+	int err, tmp, n_ssids = 0, n_filter_ssids = 0, n_channels, i;
 	u32 interval;
 	enum ieee80211_band band;
 	size_t ie_len;
@@ -3640,6 +3643,17 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
 	if (n_ssids > wiphy->max_sched_scan_ssids)
 		return -EINVAL;
 
+	if (info->attrs[NL80211_ATTR_SCHED_SCAN_FILTER]) {
+		attr = nla_find_nested(info->attrs[NL80211_ATTR_SCHED_SCAN_FILTER],
+				       NL80211_ATTR_SCHED_SCAN_FILTER_SSID);
+		if (attr)
+			nla_for_each_nested(attr2, attr, tmp)
+				n_filter_ssids++;
+	}
+
+	if (n_filter_ssids > wiphy->max_filter_ssids)
+		return -EINVAL;
+
 	if (info->attrs[NL80211_ATTR_IE])
 		ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
 	else
@@ -3657,6 +3671,7 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
 
 	request = kzalloc(sizeof(*request)
 			+ sizeof(*request->ssids) * n_ssids
+			+ sizeof(*request->filter_ssids) * n_filter_ssids
 			+ sizeof(*request->channels) * n_channels
 			+ ie_len, GFP_KERNEL);
 	if (!request) {
@@ -3674,6 +3689,17 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
 			request->ie = (void *)(request->channels + n_channels);
 	}
 
+	if (n_filter_ssids) {
+		if (request->ie)
+			request->filter_ssids = (void *)(request->ie + ie_len);
+		else if (request->ssids)
+			request->filter_ssids =
+				(void *)(request->ssids + n_ssids);
+		else
+			request->filter_ssids =
+				(void *)(request->channels + n_channels);
+	}
+
 	i = 0;
 	if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
 		/* user specified, bail out if channel not found */
@@ -3738,6 +3764,24 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
 		}
 	}
 
+	i = 0;
+	if (info->attrs[NL80211_ATTR_SCHED_SCAN_FILTER]) {
+		struct nlattr *attr2;
+		attr = nla_find_nested(info->attrs[NL80211_ATTR_SCHED_SCAN_FILTER],
+				       NL80211_ATTR_SCHED_SCAN_FILTER_SSID);
+		if (attr)
+			nla_for_each_nested(attr2, attr, tmp) {
+				if (nla_len(attr2) > IEEE80211_MAX_SSID_LEN) {
+					err = -EINVAL;
+					goto out_free;
+				}
+				memcpy(request->filter_ssids[i].ssid, nla_data(attr2),
+				       nla_len(attr2));
+				request->filter_ssids[i].ssid_len = nla_len(attr2);
+				i++;
+			}
+	}
+
 	if (info->attrs[NL80211_ATTR_IE]) {
 		request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
 		memcpy((void *)request->ie,
-- 
1.7.1

--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Host AP]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Linux Kernel]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Samba]     [Device Mapper]
  Powered by Linux