2 * Copyright (C) 2016 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include "wificond/scanning/scan_utils.h"
21 #include <linux/netlink.h>
22 #include <linux/nl80211.h>
24 #include <android-base/logging.h>
26 #include "wificond/net/netlink_manager.h"
27 #include "wificond/net/nl80211_packet.h"
28 #include "wificond/scanning/scan_result.h"
30 using com::android::server::wifi::wificond::NativeScanResult;
31 using std::unique_ptr;
38 constexpr uint8_t kElemIdSsid = 0;
42 ScanUtils::ScanUtils(NetlinkManager* netlink_manager)
43 : netlink_manager_(netlink_manager) {
44 if (!netlink_manager_->IsStarted()) {
45 netlink_manager_->Start();
49 ScanUtils::~ScanUtils() {}
51 void ScanUtils::SubscribeScanResultNotification(
52 uint32_t interface_index,
53 OnScanResultsReadyHandler handler) {
54 netlink_manager_->SubscribeScanResultNotification(interface_index, handler);
57 void ScanUtils::UnsubscribeScanResultNotification(uint32_t interface_index) {
58 netlink_manager_->UnsubscribeScanResultNotification(interface_index);
61 void ScanUtils::SubscribeSchedScanResultNotification(
62 uint32_t interface_index,
63 OnSchedScanResultsReadyHandler handler) {
64 netlink_manager_->SubscribeSchedScanResultNotification(interface_index,
68 void ScanUtils::UnsubscribeSchedScanResultNotification(
69 uint32_t interface_index) {
70 netlink_manager_->UnsubscribeSchedScanResultNotification(interface_index);
73 bool ScanUtils::GetScanResult(uint32_t interface_index,
74 vector<NativeScanResult>* out_scan_results) {
75 NL80211Packet get_scan(
76 netlink_manager_->GetFamilyId(),
78 netlink_manager_->GetSequenceNumber(),
80 get_scan.AddFlag(NLM_F_DUMP);
81 NL80211Attr<uint32_t> ifindex(NL80211_ATTR_IFINDEX, interface_index);
82 get_scan.AddAttribute(ifindex);
84 vector<unique_ptr<const NL80211Packet>> response;
85 if (!netlink_manager_->SendMessageAndGetResponses(get_scan, &response)) {
86 LOG(ERROR) << "NL80211_CMD_GET_SCAN dump failed";
89 if (response.empty()) {
90 LOG(INFO) << "Unexpected empty scan result!";
94 for (auto& packet : response) {
95 if (packet->GetMessageType() == NLMSG_ERROR) {
96 LOG(ERROR) << "Receive ERROR message: "
97 << strerror(packet->GetErrorCode());
100 if (packet->GetMessageType() != netlink_manager_->GetFamilyId()) {
101 LOG(ERROR) << "Wrong message type: "
102 << packet->GetMessageType();
106 if (!packet->GetAttributeValue(NL80211_ATTR_IFINDEX, &if_index)) {
107 LOG(ERROR) << "No interface index in scan result.";
110 if (if_index != interface_index) {
111 LOG(WARNING) << "Uninteresting scan result for interface: " << if_index;
115 NativeScanResult scan_result;
116 if (!ParseScanResult(std::move(packet), &scan_result)) {
117 LOG(DEBUG) << "Ignore invalid scan result";
120 out_scan_results->push_back(std::move(scan_result));
125 bool ScanUtils::ParseScanResult(unique_ptr<const NL80211Packet> packet,
126 NativeScanResult* scan_result) {
127 if (packet->GetCommand() != NL80211_CMD_NEW_SCAN_RESULTS) {
128 LOG(ERROR) << "Wrong command command for new scan result message";
131 NL80211NestedAttr bss(0);
132 if (packet->GetAttribute(NL80211_ATTR_BSS, &bss)) {
133 vector<uint8_t> bssid;
134 if (!bss.GetAttributeValue(NL80211_BSS_BSSID, &bssid)) {
135 LOG(ERROR) << "Failed to get BSSID from scan result packet";
139 if (!bss.GetAttributeValue(NL80211_BSS_FREQUENCY, &freq)) {
140 LOG(ERROR) << "Failed to get Frequency from scan result packet";
144 if (!bss.GetAttributeValue(NL80211_BSS_INFORMATION_ELEMENTS, &ie)) {
145 LOG(ERROR) << "Failed to get Information Element from scan result packet";
148 vector<uint8_t> ssid;
149 if (!GetSSIDFromInfoElement(ie, &ssid)) {
150 // Skip BSS without SSID IE.
151 // It might be from a hidden network. Framework doesn't need it.
155 std::all_of(ssid.begin(), ssid.end(), [](uint8_t c) {return c == 0;})) {
156 // Skip BSS with empty or all-zero SSID.
157 // It might be from a hidden network. Framework doesn't need it.
161 if (!bss.GetAttributeValue(NL80211_BSS_TSF, &tsf)) {
162 LOG(ERROR) << "Failed to get TSF from scan result packet";
166 if (bss.GetAttributeValue(NL80211_BSS_BEACON_TSF, &beacon_tsf)) {
167 if (beacon_tsf > tsf) {
172 if (!bss.GetAttributeValue(NL80211_BSS_SIGNAL_MBM, &signal)) {
173 LOG(ERROR) << "Failed to get Signal Strength from scan result packet";
177 if (!bss.GetAttributeValue(NL80211_BSS_CAPABILITY, &capability)) {
178 LOG(ERROR) << "Failed to get capability field from scan result packet";
181 bool associated = false;
183 if (bss.GetAttributeValue(NL80211_BSS_STATUS, &bss_status) &&
184 (bss_status == NL80211_BSS_STATUS_AUTHENTICATED ||
185 bss_status == NL80211_BSS_STATUS_ASSOCIATED)) {
190 NativeScanResult(ssid, bssid, ie, freq, signal, tsf, capability, associated);
195 bool ScanUtils::GetSSIDFromInfoElement(const vector<uint8_t>& ie,
196 vector<uint8_t>* ssid) {
197 // Information elements are stored in 'TLV' format.
198 // Field: | Type | Length | Value |
199 // Length: | 1 | 1 | variable |
200 // Content:| Element ID | Length of the Value field | Element payload |
201 const uint8_t* end = ie.data() + ie.size();
202 const uint8_t* ptr = ie.data();
203 // +1 means we must have space for the length field.
204 while (ptr + 1 < end) {
206 uint8_t length = *(ptr + 1);
207 // Length field is invalid.
208 if (ptr + 1 + length >= end) {
211 // SSID element is found.
212 if (type == kElemIdSsid) {
213 // SSID is an empty string.
215 *ssid = vector<uint8_t>();
217 *ssid = vector<uint8_t>(ptr + 2, ptr + length + 2);
226 bool ScanUtils::Scan(uint32_t interface_index,
227 bool request_random_mac,
228 const vector<vector<uint8_t>>& ssids,
229 const vector<uint32_t>& freqs) {
230 NL80211Packet trigger_scan(
231 netlink_manager_->GetFamilyId(),
232 NL80211_CMD_TRIGGER_SCAN,
233 netlink_manager_->GetSequenceNumber(),
235 // If we do not use NLM_F_ACK, we only receive a unicast repsonse
236 // when there is an error. If everything is good, scan results notification
237 // will only be sent through multicast.
238 // If NLM_F_ACK is set, there will always be an unicast repsonse, either an
239 // ERROR or an ACK message. The handler will always be called and removed by
241 trigger_scan.AddFlag(NLM_F_ACK);
242 NL80211Attr<uint32_t> if_index_attr(NL80211_ATTR_IFINDEX, interface_index);
244 NL80211NestedAttr ssids_attr(NL80211_ATTR_SCAN_SSIDS);
245 for (size_t i = 0; i < ssids.size(); i++) {
246 ssids_attr.AddAttribute(NL80211Attr<vector<uint8_t>>(i, ssids[i]));
248 NL80211NestedAttr freqs_attr(NL80211_ATTR_SCAN_FREQUENCIES);
249 for (size_t i = 0; i < freqs.size(); i++) {
250 freqs_attr.AddAttribute(NL80211Attr<uint32_t>(i, freqs[i]));
253 trigger_scan.AddAttribute(if_index_attr);
254 trigger_scan.AddAttribute(ssids_attr);
255 // An absence of NL80211_ATTR_SCAN_FREQUENCIES attribue informs kernel to
256 // scan all supported frequencies.
257 if (!freqs.empty()) {
258 trigger_scan.AddAttribute(freqs_attr);
261 if (request_random_mac) {
262 trigger_scan.AddAttribute(
263 NL80211Attr<uint32_t>(NL80211_ATTR_SCAN_FLAGS,
264 NL80211_SCAN_FLAG_RANDOM_ADDR));
266 // We are receiving an ERROR/ACK message instead of the actual
267 // scan results here, so it is OK to expect a timely response because
268 // kernel is supposed to send the ERROR/ACK back before the scan starts.
269 vector<unique_ptr<const NL80211Packet>> response;
270 if (!netlink_manager_->SendMessageAndGetAck(trigger_scan)) {
271 LOG(ERROR) << "NL80211_CMD_TRIGGER_SCAN failed";
277 bool ScanUtils::StopScheduledScan(uint32_t interface_index) {
278 NL80211Packet stop_sched_scan(
279 netlink_manager_->GetFamilyId(),
280 NL80211_CMD_STOP_SCHED_SCAN,
281 netlink_manager_->GetSequenceNumber(),
283 // Force an ACK response upon success.
284 stop_sched_scan.AddFlag(NLM_F_ACK);
285 stop_sched_scan.AddAttribute(
286 NL80211Attr<uint32_t>(NL80211_ATTR_IFINDEX, interface_index));
287 vector<unique_ptr<const NL80211Packet>> response;
289 if (!netlink_manager_->SendMessageAndGetAckOrError(stop_sched_scan,
291 LOG(ERROR) << "NL80211_CMD_STOP_SCHED_SCAN failed";
294 if (error_code == ENOENT) {
295 LOG(WARNING) << "Scheduled scan is not running!";
297 } else if (error_code != 0) {
298 LOG(ERROR) << "Receive ERROR message in response to"
299 << " 'stop scheduled scan' request: "
300 << strerror(error_code);
306 bool ScanUtils::StartScheduledScan(
307 uint32_t interface_index,
308 uint32_t interval_ms,
309 int32_t rssi_threshold,
310 bool request_random_mac,
311 const std::vector<std::vector<uint8_t>>& scan_ssids,
312 const std::vector<std::vector<uint8_t>>& match_ssids,
313 const std::vector<uint32_t>& freqs) {
314 NL80211Packet start_sched_scan(
315 netlink_manager_->GetFamilyId(),
316 NL80211_CMD_START_SCHED_SCAN,
317 netlink_manager_->GetSequenceNumber(),
319 // Force an ACK response upon success.
320 start_sched_scan.AddFlag(NLM_F_ACK);
322 NL80211NestedAttr scan_ssids_attr(NL80211_ATTR_SCAN_SSIDS);
323 for (size_t i = 0; i < scan_ssids.size(); i++) {
324 scan_ssids_attr.AddAttribute(NL80211Attr<vector<uint8_t>>(i, scan_ssids[i]));
326 NL80211NestedAttr freqs_attr(NL80211_ATTR_SCAN_FREQUENCIES);
327 for (size_t i = 0; i < freqs.size(); i++) {
328 freqs_attr.AddAttribute(NL80211Attr<uint32_t>(i, freqs[i]));
331 // Structure of attributes of scheduled scan filters:
332 // | Nested Attribute: id: NL80211_ATTR_SCHED_SCAN_MATCH |
333 // | Nested Attributed: id: 0 | Nested Attributed: id: 1 | Nested Attr: id: 2 | ... |
334 // | MATCH_SSID | MATCH_RSSI(optional) | MATCH_SSID | MACTCH_RSSI(optional) | MATCH_RSSI(optinal, global) | ... |
335 NL80211NestedAttr scan_match_attr(NL80211_ATTR_SCHED_SCAN_MATCH);
336 for (size_t i = 0; i < match_ssids.size(); i++) {
337 NL80211NestedAttr match_group(i);
338 match_group.AddAttribute(
339 NL80211Attr<vector<uint8_t>>(NL80211_SCHED_SCAN_MATCH_ATTR_SSID, match_ssids[i]));
340 match_group.AddAttribute(
341 NL80211Attr<int32_t>(NL80211_SCHED_SCAN_MATCH_ATTR_RSSI, rssi_threshold));
342 scan_match_attr.AddAttribute(match_group);
345 // Append all attributes to the NL80211_CMD_START_SCHED_SCAN packet.
346 start_sched_scan.AddAttribute(
347 NL80211Attr<uint32_t>(NL80211_ATTR_IFINDEX, interface_index));
348 start_sched_scan.AddAttribute(scan_ssids_attr);
349 // An absence of NL80211_ATTR_SCAN_FREQUENCIES attribue informs kernel to
350 // scan all supported frequencies.
351 if (!freqs.empty()) {
352 start_sched_scan.AddAttribute(freqs_attr);
354 start_sched_scan.AddAttribute(
355 NL80211Attr<uint32_t>(NL80211_ATTR_SCHED_SCAN_INTERVAL, interval_ms));
356 start_sched_scan.AddAttribute(scan_match_attr);
357 if (request_random_mac) {
358 start_sched_scan.AddAttribute(
359 NL80211Attr<uint32_t>(NL80211_ATTR_SCAN_FLAGS,
360 NL80211_SCAN_FLAG_RANDOM_ADDR));
363 vector<unique_ptr<const NL80211Packet>> response;
364 if (!netlink_manager_->SendMessageAndGetAck(start_sched_scan)) {
365 LOG(ERROR) << "NL80211_CMD_START_SCHED_SCAN failed";
372 } // namespace wificond
373 } // namespace android