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;
39 constexpr unsigned int kMsecPerSec = 1000;
43 ScanUtils::ScanUtils(NetlinkManager* netlink_manager)
44 : netlink_manager_(netlink_manager) {
45 if (!netlink_manager_->IsStarted()) {
46 netlink_manager_->Start();
50 ScanUtils::~ScanUtils() {}
52 void ScanUtils::SubscribeScanResultNotification(
53 uint32_t interface_index,
54 OnScanResultsReadyHandler handler) {
55 netlink_manager_->SubscribeScanResultNotification(interface_index, handler);
58 void ScanUtils::UnsubscribeScanResultNotification(uint32_t interface_index) {
59 netlink_manager_->UnsubscribeScanResultNotification(interface_index);
62 void ScanUtils::SubscribeSchedScanResultNotification(
63 uint32_t interface_index,
64 OnSchedScanResultsReadyHandler handler) {
65 netlink_manager_->SubscribeSchedScanResultNotification(interface_index,
69 void ScanUtils::UnsubscribeSchedScanResultNotification(
70 uint32_t interface_index) {
71 netlink_manager_->UnsubscribeSchedScanResultNotification(interface_index);
74 bool ScanUtils::GetScanResult(uint32_t interface_index,
75 vector<NativeScanResult>* out_scan_results) {
76 NL80211Packet get_scan(
77 netlink_manager_->GetFamilyId(),
79 netlink_manager_->GetSequenceNumber(),
81 get_scan.AddFlag(NLM_F_DUMP);
82 NL80211Attr<uint32_t> ifindex(NL80211_ATTR_IFINDEX, interface_index);
83 get_scan.AddAttribute(ifindex);
85 vector<unique_ptr<const NL80211Packet>> response;
86 if (!netlink_manager_->SendMessageAndGetResponses(get_scan, &response)) {
87 LOG(ERROR) << "NL80211_CMD_GET_SCAN dump failed";
90 if (response.empty()) {
91 LOG(INFO) << "Unexpected empty scan result!";
95 for (auto& packet : response) {
96 if (packet->GetMessageType() == NLMSG_ERROR) {
97 LOG(ERROR) << "Receive ERROR message: "
98 << strerror(packet->GetErrorCode());
101 if (packet->GetMessageType() != netlink_manager_->GetFamilyId()) {
102 LOG(ERROR) << "Wrong message type: "
103 << packet->GetMessageType();
107 if (!packet->GetAttributeValue(NL80211_ATTR_IFINDEX, &if_index)) {
108 LOG(ERROR) << "No interface index in scan result.";
111 if (if_index != interface_index) {
112 LOG(WARNING) << "Uninteresting scan result for interface: " << if_index;
116 NativeScanResult scan_result;
117 if (!ParseScanResult(std::move(packet), &scan_result)) {
118 LOG(DEBUG) << "Ignore invalid scan result";
121 out_scan_results->push_back(std::move(scan_result));
126 bool ScanUtils::ParseScanResult(unique_ptr<const NL80211Packet> packet,
127 NativeScanResult* scan_result) {
128 if (packet->GetCommand() != NL80211_CMD_NEW_SCAN_RESULTS) {
129 LOG(ERROR) << "Wrong command command for new scan result message";
132 NL80211NestedAttr bss(0);
133 if (packet->GetAttribute(NL80211_ATTR_BSS, &bss)) {
134 vector<uint8_t> bssid;
135 if (!bss.GetAttributeValue(NL80211_BSS_BSSID, &bssid)) {
136 LOG(ERROR) << "Failed to get BSSID from scan result packet";
140 if (!bss.GetAttributeValue(NL80211_BSS_FREQUENCY, &freq)) {
141 LOG(ERROR) << "Failed to get Frequency from scan result packet";
145 if (!bss.GetAttributeValue(NL80211_BSS_INFORMATION_ELEMENTS, &ie)) {
146 LOG(ERROR) << "Failed to get Information Element from scan result packet";
149 vector<uint8_t> ssid;
150 if (!GetSSIDFromInfoElement(ie, &ssid)) {
151 // Skip BSS without SSID IE.
152 // These scan results are considered as malformed.
155 uint64_t last_seen_since_boot_microseconds;
156 if (!GetBssTimestamp(bss, &last_seen_since_boot_microseconds)) {
157 // Logging is done inside |GetBssTimestamp|.
161 if (!bss.GetAttributeValue(NL80211_BSS_SIGNAL_MBM, &signal)) {
162 LOG(ERROR) << "Failed to get Signal Strength from scan result packet";
166 if (!bss.GetAttributeValue(NL80211_BSS_CAPABILITY, &capability)) {
167 LOG(ERROR) << "Failed to get capability field from scan result packet";
170 bool associated = false;
172 if (bss.GetAttributeValue(NL80211_BSS_STATUS, &bss_status) &&
173 (bss_status == NL80211_BSS_STATUS_AUTHENTICATED ||
174 bss_status == NL80211_BSS_STATUS_ASSOCIATED)) {
179 NativeScanResult(ssid, bssid, ie, freq, signal,
180 last_seen_since_boot_microseconds,
181 capability, associated);
186 bool ScanUtils::GetBssTimestampForTesting(
187 const NL80211NestedAttr& bss,
188 uint64_t* last_seen_since_boot_microseconds){
189 return GetBssTimestamp(bss, last_seen_since_boot_microseconds);
192 bool ScanUtils::GetBssTimestamp(const NL80211NestedAttr& bss,
193 uint64_t* last_seen_since_boot_microseconds){
194 uint64_t last_seen_since_boot_nanoseconds;
195 if (bss.GetAttributeValue(NL80211_BSS_LAST_SEEN_BOOTTIME,
196 &last_seen_since_boot_nanoseconds)) {
197 *last_seen_since_boot_microseconds = last_seen_since_boot_nanoseconds / 1000;
199 // Fall back to use TSF if we can't find NL80211_BSS_LAST_SEEN_BOOTTIME
201 if (!bss.GetAttributeValue(NL80211_BSS_TSF, last_seen_since_boot_microseconds)) {
202 LOG(ERROR) << "Failed to get TSF from scan result packet";
205 uint64_t beacon_tsf_microseconds;
206 if (bss.GetAttributeValue(NL80211_BSS_BEACON_TSF, &beacon_tsf_microseconds)) {
207 *last_seen_since_boot_microseconds = std::max(*last_seen_since_boot_microseconds,
208 beacon_tsf_microseconds);
214 bool ScanUtils::GetSSIDFromInfoElement(const vector<uint8_t>& ie,
215 vector<uint8_t>* ssid) {
216 // Information elements are stored in 'TLV' format.
217 // Field: | Type | Length | Value |
218 // Length: | 1 | 1 | variable |
219 // Content:| Element ID | Length of the Value field | Element payload |
220 const uint8_t* end = ie.data() + ie.size();
221 const uint8_t* ptr = ie.data();
222 // +1 means we must have space for the length field.
223 while (ptr + 1 < end) {
225 uint8_t length = *(ptr + 1);
226 // Length field is invalid.
227 if (ptr + 1 + length >= end) {
230 // SSID element is found.
231 if (type == kElemIdSsid) {
232 // SSID is an empty string.
234 *ssid = vector<uint8_t>();
236 *ssid = vector<uint8_t>(ptr + 2, ptr + length + 2);
245 bool ScanUtils::Scan(uint32_t interface_index,
246 bool request_random_mac,
247 const vector<vector<uint8_t>>& ssids,
248 const vector<uint32_t>& freqs,
250 NL80211Packet trigger_scan(
251 netlink_manager_->GetFamilyId(),
252 NL80211_CMD_TRIGGER_SCAN,
253 netlink_manager_->GetSequenceNumber(),
255 // If we do not use NLM_F_ACK, we only receive a unicast repsonse
256 // when there is an error. If everything is good, scan results notification
257 // will only be sent through multicast.
258 // If NLM_F_ACK is set, there will always be an unicast repsonse, either an
259 // ERROR or an ACK message. The handler will always be called and removed by
261 trigger_scan.AddFlag(NLM_F_ACK);
262 NL80211Attr<uint32_t> if_index_attr(NL80211_ATTR_IFINDEX, interface_index);
264 NL80211NestedAttr ssids_attr(NL80211_ATTR_SCAN_SSIDS);
265 for (size_t i = 0; i < ssids.size(); i++) {
266 ssids_attr.AddAttribute(NL80211Attr<vector<uint8_t>>(i, ssids[i]));
268 NL80211NestedAttr freqs_attr(NL80211_ATTR_SCAN_FREQUENCIES);
269 for (size_t i = 0; i < freqs.size(); i++) {
270 freqs_attr.AddAttribute(NL80211Attr<uint32_t>(i, freqs[i]));
273 trigger_scan.AddAttribute(if_index_attr);
274 trigger_scan.AddAttribute(ssids_attr);
275 // An absence of NL80211_ATTR_SCAN_FREQUENCIES attribue informs kernel to
276 // scan all supported frequencies.
277 if (!freqs.empty()) {
278 trigger_scan.AddAttribute(freqs_attr);
281 if (request_random_mac) {
282 trigger_scan.AddAttribute(
283 NL80211Attr<uint32_t>(NL80211_ATTR_SCAN_FLAGS,
284 NL80211_SCAN_FLAG_RANDOM_ADDR));
286 // We are receiving an ERROR/ACK message instead of the actual
287 // scan results here, so it is OK to expect a timely response because
288 // kernel is supposed to send the ERROR/ACK back before the scan starts.
289 vector<unique_ptr<const NL80211Packet>> response;
290 if (!netlink_manager_->SendMessageAndGetAckOrError(trigger_scan,
292 // Logging is done inside |SendMessageAndGetAckOrError|.
295 if (*error_code != 0) {
296 LOG(ERROR) << "NL80211_CMD_TRIGGER_SCAN failed: " << strerror(*error_code);
302 bool ScanUtils::StopScheduledScan(uint32_t interface_index) {
303 NL80211Packet stop_sched_scan(
304 netlink_manager_->GetFamilyId(),
305 NL80211_CMD_STOP_SCHED_SCAN,
306 netlink_manager_->GetSequenceNumber(),
308 // Force an ACK response upon success.
309 stop_sched_scan.AddFlag(NLM_F_ACK);
310 stop_sched_scan.AddAttribute(
311 NL80211Attr<uint32_t>(NL80211_ATTR_IFINDEX, interface_index));
312 vector<unique_ptr<const NL80211Packet>> response;
314 if (!netlink_manager_->SendMessageAndGetAckOrError(stop_sched_scan,
316 LOG(ERROR) << "NL80211_CMD_STOP_SCHED_SCAN failed";
319 if (error_code == ENOENT) {
320 LOG(WARNING) << "Scheduled scan is not running!";
322 } else if (error_code != 0) {
323 LOG(ERROR) << "Receive ERROR message in response to"
324 << " 'stop scheduled scan' request: "
325 << strerror(error_code);
331 bool ScanUtils::AbortScan(uint32_t interface_index) {
332 NL80211Packet abort_scan(
333 netlink_manager_->GetFamilyId(),
334 NL80211_CMD_ABORT_SCAN,
335 netlink_manager_->GetSequenceNumber(),
338 // Force an ACK response upon success.
339 abort_scan.AddFlag(NLM_F_ACK);
340 abort_scan.AddAttribute(
341 NL80211Attr<uint32_t>(NL80211_ATTR_IFINDEX, interface_index));
343 if (!netlink_manager_->SendMessageAndGetAck(abort_scan)) {
344 LOG(ERROR) << "NL80211_CMD_ABORT_SCAN failed";
350 bool ScanUtils::StartScheduledScan(
351 uint32_t interface_index,
352 const SchedScanIntervalSetting& interval_setting,
353 int32_t rssi_threshold,
354 bool request_random_mac,
355 const std::vector<std::vector<uint8_t>>& scan_ssids,
356 const std::vector<std::vector<uint8_t>>& match_ssids,
357 const std::vector<uint32_t>& freqs,
359 NL80211Packet start_sched_scan(
360 netlink_manager_->GetFamilyId(),
361 NL80211_CMD_START_SCHED_SCAN,
362 netlink_manager_->GetSequenceNumber(),
364 // Force an ACK response upon success.
365 start_sched_scan.AddFlag(NLM_F_ACK);
367 NL80211NestedAttr scan_ssids_attr(NL80211_ATTR_SCAN_SSIDS);
368 for (size_t i = 0; i < scan_ssids.size(); i++) {
369 scan_ssids_attr.AddAttribute(NL80211Attr<vector<uint8_t>>(i, scan_ssids[i]));
371 NL80211NestedAttr freqs_attr(NL80211_ATTR_SCAN_FREQUENCIES);
372 for (size_t i = 0; i < freqs.size(); i++) {
373 freqs_attr.AddAttribute(NL80211Attr<uint32_t>(i, freqs[i]));
376 // Structure of attributes of scheduled scan filters:
377 // | Nested Attribute: id: NL80211_ATTR_SCHED_SCAN_MATCH |
378 // | Nested Attributed: id: 0 | Nested Attributed: id: 1 | Nested Attr: id: 2 | ... |
379 // | MATCH_SSID | MATCH_RSSI(optional) | MATCH_SSID | MACTCH_RSSI(optional) | MATCH_RSSI(optinal, global) | ... |
380 NL80211NestedAttr scan_match_attr(NL80211_ATTR_SCHED_SCAN_MATCH);
381 for (size_t i = 0; i < match_ssids.size(); i++) {
382 NL80211NestedAttr match_group(i);
383 match_group.AddAttribute(
384 NL80211Attr<vector<uint8_t>>(NL80211_SCHED_SCAN_MATCH_ATTR_SSID, match_ssids[i]));
385 match_group.AddAttribute(
386 NL80211Attr<int32_t>(NL80211_SCHED_SCAN_MATCH_ATTR_RSSI, rssi_threshold));
387 scan_match_attr.AddAttribute(match_group);
389 start_sched_scan.AddAttribute(scan_match_attr);
391 // Append all attributes to the NL80211_CMD_START_SCHED_SCAN packet.
392 start_sched_scan.AddAttribute(
393 NL80211Attr<uint32_t>(NL80211_ATTR_IFINDEX, interface_index));
394 start_sched_scan.AddAttribute(scan_ssids_attr);
395 // An absence of NL80211_ATTR_SCAN_FREQUENCIES attribue informs kernel to
396 // scan all supported frequencies.
397 if (!freqs.empty()) {
398 start_sched_scan.AddAttribute(freqs_attr);
401 if (!interval_setting.plans.empty()) {
402 NL80211NestedAttr scan_plans(NL80211_ATTR_SCHED_SCAN_PLANS);
403 for (unsigned int i = 0; i < interval_setting.plans.size(); i++) {
404 NL80211NestedAttr scan_plan(i + 1);
405 scan_plan.AddAttribute(
406 NL80211Attr<uint32_t>(NL80211_SCHED_SCAN_PLAN_INTERVAL,
407 interval_setting.plans[i].interval_ms / kMsecPerSec));
408 scan_plan.AddAttribute(
409 NL80211Attr<uint32_t>(NL80211_SCHED_SCAN_PLAN_ITERATIONS,
410 interval_setting.plans[i].n_iterations));
411 scan_plans.AddAttribute(scan_plan);
413 NL80211NestedAttr last_scan_plan(interval_setting.plans.size() + 1);
414 last_scan_plan.AddAttribute(
415 NL80211Attr<uint32_t>(NL80211_SCHED_SCAN_PLAN_INTERVAL,
416 interval_setting.final_interval_ms / kMsecPerSec));
417 scan_plans.AddAttribute(last_scan_plan);
418 start_sched_scan.AddAttribute(scan_plans);
420 start_sched_scan.AddAttribute(
421 NL80211Attr<uint32_t>(NL80211_ATTR_SCHED_SCAN_INTERVAL,
422 interval_setting.final_interval_ms));
425 if (request_random_mac) {
426 start_sched_scan.AddAttribute(
427 NL80211Attr<uint32_t>(NL80211_ATTR_SCAN_FLAGS,
428 NL80211_SCAN_FLAG_RANDOM_ADDR));
431 vector<unique_ptr<const NL80211Packet>> response;
432 if (!netlink_manager_->SendMessageAndGetAckOrError(start_sched_scan,
434 // Logging is done inside |SendMessageAndGetAckOrError|.
437 if (*error_code != 0) {
438 LOG(ERROR) << "NL80211_CMD_START_SCHED_SCAN failed: " << strerror(*error_code);
445 } // namespace wificond
446 } // namespace android