Home | History | Annotate | Download | only in scanning
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      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
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     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.
     15  */
     16 
     17 #include "wificond/scanning/scan_utils.h"
     18 
     19 #include <vector>
     20 
     21 #include <linux/netlink.h>
     22 #include <linux/nl80211.h>
     23 
     24 #include <android-base/logging.h>
     25 
     26 #include "wificond/net/netlink_manager.h"
     27 #include "wificond/net/nl80211_packet.h"
     28 #include "wificond/scanning/scan_result.h"
     29 
     30 using com::android::server::wifi::wificond::NativeScanResult;
     31 using std::unique_ptr;
     32 using std::vector;
     33 
     34 namespace android {
     35 namespace wificond {
     36 namespace {
     37 
     38 constexpr uint8_t kElemIdSsid = 0;
     39 
     40 }  // namespace
     41 
     42 ScanUtils::ScanUtils(NetlinkManager* netlink_manager)
     43     : netlink_manager_(netlink_manager) {
     44   if (!netlink_manager_->IsStarted()) {
     45     netlink_manager_->Start();
     46   }
     47 }
     48 
     49 ScanUtils::~ScanUtils() {}
     50 
     51 void ScanUtils::SubscribeScanResultNotification(
     52     uint32_t interface_index,
     53     OnScanResultsReadyHandler handler) {
     54   netlink_manager_->SubscribeScanResultNotification(interface_index, handler);
     55 }
     56 
     57 void ScanUtils::UnsubscribeScanResultNotification(uint32_t interface_index) {
     58   netlink_manager_->UnsubscribeScanResultNotification(interface_index);
     59 }
     60 
     61 void ScanUtils::SubscribeSchedScanResultNotification(
     62     uint32_t interface_index,
     63     OnSchedScanResultsReadyHandler handler) {
     64   netlink_manager_->SubscribeSchedScanResultNotification(interface_index,
     65                                                          handler);
     66 }
     67 
     68 void ScanUtils::UnsubscribeSchedScanResultNotification(
     69     uint32_t interface_index) {
     70   netlink_manager_->UnsubscribeSchedScanResultNotification(interface_index);
     71 }
     72 
     73 bool ScanUtils::GetScanResult(uint32_t interface_index,
     74                               vector<NativeScanResult>* out_scan_results) {
     75   NL80211Packet get_scan(
     76       netlink_manager_->GetFamilyId(),
     77       NL80211_CMD_GET_SCAN,
     78       netlink_manager_->GetSequenceNumber(),
     79       getpid());
     80   get_scan.AddFlag(NLM_F_DUMP);
     81   NL80211Attr<uint32_t> ifindex(NL80211_ATTR_IFINDEX, interface_index);
     82   get_scan.AddAttribute(ifindex);
     83 
     84   vector<unique_ptr<const NL80211Packet>> response;
     85   if (!netlink_manager_->SendMessageAndGetResponses(get_scan, &response))  {
     86     LOG(ERROR) << "NL80211_CMD_GET_SCAN dump failed";
     87     return false;
     88   }
     89   if (response.empty()) {
     90     LOG(INFO) << "Unexpected empty scan result!";
     91     return true;
     92   }
     93 
     94   for (auto& packet : response) {
     95     if (packet->GetMessageType() == NLMSG_ERROR) {
     96       LOG(ERROR) << "Receive ERROR message: "
     97                  << strerror(packet->GetErrorCode());
     98       continue;
     99     }
    100     if (packet->GetMessageType() != netlink_manager_->GetFamilyId()) {
    101       LOG(ERROR) << "Wrong message type: "
    102                  << packet->GetMessageType();
    103       continue;
    104     }
    105     uint32_t if_index;
    106     if (!packet->GetAttributeValue(NL80211_ATTR_IFINDEX, &if_index)) {
    107       LOG(ERROR) << "No interface index in scan result.";
    108       continue;
    109     }
    110     if (if_index != interface_index) {
    111       LOG(WARNING) << "Uninteresting scan result for interface: " << if_index;
    112       continue;
    113     }
    114 
    115     NativeScanResult scan_result;
    116     if (!ParseScanResult(std::move(packet), &scan_result)) {
    117       LOG(DEBUG) << "Ignore invalid scan result";
    118       continue;
    119     }
    120     out_scan_results->push_back(std::move(scan_result));
    121   }
    122   return true;
    123 }
    124 
    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";
    129     return false;
    130   }
    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";
    136       return false;
    137     }
    138     uint32_t freq;
    139     if (!bss.GetAttributeValue(NL80211_BSS_FREQUENCY, &freq)) {
    140       LOG(ERROR) << "Failed to get Frequency from scan result packet";
    141       return false;
    142     }
    143     vector<uint8_t> ie;
    144     if (!bss.GetAttributeValue(NL80211_BSS_INFORMATION_ELEMENTS, &ie)) {
    145       LOG(ERROR) << "Failed to get Information Element from scan result packet";
    146       return false;
    147     }
    148     vector<uint8_t> ssid;
    149     if (!GetSSIDFromInfoElement(ie, &ssid)) {
    150       // Skip BSS without SSID IE.
    151       // These scan results are considered as malformed.
    152       return false;
    153     }
    154     uint64_t tsf;
    155     if (!bss.GetAttributeValue(NL80211_BSS_TSF, &tsf)) {
    156       LOG(ERROR) << "Failed to get TSF from scan result packet";
    157       return false;
    158     }
    159     uint64_t beacon_tsf;
    160     if (bss.GetAttributeValue(NL80211_BSS_BEACON_TSF, &beacon_tsf)) {
    161       if (beacon_tsf > tsf) {
    162         tsf = beacon_tsf;
    163       }
    164     }
    165     int32_t signal;
    166     if (!bss.GetAttributeValue(NL80211_BSS_SIGNAL_MBM, &signal)) {
    167       LOG(ERROR) << "Failed to get Signal Strength from scan result packet";
    168       return false;
    169     }
    170     uint16_t capability;
    171     if (!bss.GetAttributeValue(NL80211_BSS_CAPABILITY, &capability)) {
    172       LOG(ERROR) << "Failed to get capability field from scan result packet";
    173       return false;
    174     }
    175     bool associated = false;
    176     uint32_t bss_status;
    177     if (bss.GetAttributeValue(NL80211_BSS_STATUS, &bss_status) &&
    178             (bss_status == NL80211_BSS_STATUS_AUTHENTICATED ||
    179                 bss_status == NL80211_BSS_STATUS_ASSOCIATED)) {
    180       associated = true;
    181     }
    182 
    183     *scan_result =
    184         NativeScanResult(ssid, bssid, ie, freq, signal, tsf, capability, associated);
    185   }
    186   return true;
    187 }
    188 
    189 bool ScanUtils::GetSSIDFromInfoElement(const vector<uint8_t>& ie,
    190                                        vector<uint8_t>* ssid) {
    191   // Information elements are stored in 'TLV' format.
    192   // Field:  |   Type     |          Length           |      Value      |
    193   // Length: |     1      |             1             |     variable    |
    194   // Content:| Element ID | Length of the Value field | Element payload |
    195   const uint8_t* end = ie.data() + ie.size();
    196   const uint8_t* ptr = ie.data();
    197   // +1 means we must have space for the length field.
    198   while (ptr + 1  < end) {
    199     uint8_t type = *ptr;
    200     uint8_t length = *(ptr + 1);
    201     // Length field is invalid.
    202     if (ptr + 1 + length >= end) {
    203       return false;
    204     }
    205     // SSID element is found.
    206     if (type == kElemIdSsid) {
    207       // SSID is an empty string.
    208       if (length == 0) {
    209         *ssid = vector<uint8_t>();
    210       } else {
    211         *ssid = vector<uint8_t>(ptr + 2, ptr + length + 2);
    212       }
    213       return true;
    214     }
    215     ptr += 2 + length;
    216   }
    217   return false;
    218 }
    219 
    220 bool ScanUtils::Scan(uint32_t interface_index,
    221                      bool request_random_mac,
    222                      const vector<vector<uint8_t>>& ssids,
    223                      const vector<uint32_t>& freqs) {
    224   NL80211Packet trigger_scan(
    225       netlink_manager_->GetFamilyId(),
    226       NL80211_CMD_TRIGGER_SCAN,
    227       netlink_manager_->GetSequenceNumber(),
    228       getpid());
    229   // If we do not use NLM_F_ACK, we only receive a unicast repsonse
    230   // when there is an error. If everything is good, scan results notification
    231   // will only be sent through multicast.
    232   // If NLM_F_ACK is set, there will always be an unicast repsonse, either an
    233   // ERROR or an ACK message. The handler will always be called and removed by
    234   // NetlinkManager.
    235   trigger_scan.AddFlag(NLM_F_ACK);
    236   NL80211Attr<uint32_t> if_index_attr(NL80211_ATTR_IFINDEX, interface_index);
    237 
    238   NL80211NestedAttr ssids_attr(NL80211_ATTR_SCAN_SSIDS);
    239   for (size_t i = 0; i < ssids.size(); i++) {
    240     ssids_attr.AddAttribute(NL80211Attr<vector<uint8_t>>(i, ssids[i]));
    241   }
    242   NL80211NestedAttr freqs_attr(NL80211_ATTR_SCAN_FREQUENCIES);
    243   for (size_t i = 0; i < freqs.size(); i++) {
    244     freqs_attr.AddAttribute(NL80211Attr<uint32_t>(i, freqs[i]));
    245   }
    246 
    247   trigger_scan.AddAttribute(if_index_attr);
    248   trigger_scan.AddAttribute(ssids_attr);
    249   // An absence of NL80211_ATTR_SCAN_FREQUENCIES attribue informs kernel to
    250   // scan all supported frequencies.
    251   if (!freqs.empty()) {
    252     trigger_scan.AddAttribute(freqs_attr);
    253   }
    254 
    255   if (request_random_mac) {
    256     trigger_scan.AddAttribute(
    257         NL80211Attr<uint32_t>(NL80211_ATTR_SCAN_FLAGS,
    258                               NL80211_SCAN_FLAG_RANDOM_ADDR));
    259   }
    260   // We are receiving an ERROR/ACK message instead of the actual
    261   // scan results here, so it is OK to expect a timely response because
    262   // kernel is supposed to send the ERROR/ACK back before the scan starts.
    263   vector<unique_ptr<const NL80211Packet>> response;
    264   if (!netlink_manager_->SendMessageAndGetAck(trigger_scan)) {
    265     LOG(ERROR) << "NL80211_CMD_TRIGGER_SCAN failed";
    266     return false;
    267   }
    268   return true;
    269 }
    270 
    271 bool ScanUtils::StopScheduledScan(uint32_t interface_index) {
    272   NL80211Packet stop_sched_scan(
    273       netlink_manager_->GetFamilyId(),
    274       NL80211_CMD_STOP_SCHED_SCAN,
    275       netlink_manager_->GetSequenceNumber(),
    276       getpid());
    277   // Force an ACK response upon success.
    278   stop_sched_scan.AddFlag(NLM_F_ACK);
    279   stop_sched_scan.AddAttribute(
    280       NL80211Attr<uint32_t>(NL80211_ATTR_IFINDEX, interface_index));
    281   vector<unique_ptr<const NL80211Packet>> response;
    282   int error_code;
    283   if (!netlink_manager_->SendMessageAndGetAckOrError(stop_sched_scan,
    284                                                      &error_code))  {
    285     LOG(ERROR) << "NL80211_CMD_STOP_SCHED_SCAN failed";
    286     return false;
    287   }
    288   if (error_code == ENOENT) {
    289     LOG(WARNING) << "Scheduled scan is not running!";
    290     return false;
    291   } else if (error_code != 0) {
    292     LOG(ERROR) << "Receive ERROR message in response to"
    293                << " 'stop scheduled scan' request: "
    294                << strerror(error_code);
    295     return false;
    296   }
    297   return true;
    298 }
    299 
    300 bool ScanUtils::AbortScan(uint32_t interface_index) {
    301   NL80211Packet abort_scan(
    302       netlink_manager_->GetFamilyId(),
    303       NL80211_CMD_ABORT_SCAN,
    304       netlink_manager_->GetSequenceNumber(),
    305       getpid());
    306 
    307   // Force an ACK response upon success.
    308   abort_scan.AddFlag(NLM_F_ACK);
    309   abort_scan.AddAttribute(
    310       NL80211Attr<uint32_t>(NL80211_ATTR_IFINDEX, interface_index));
    311 
    312   if (!netlink_manager_->SendMessageAndGetAck(abort_scan)) {
    313     LOG(ERROR) << "NL80211_CMD_ABORT_SCAN failed";
    314     return false;
    315   }
    316   return true;
    317 }
    318 
    319 bool ScanUtils::StartScheduledScan(
    320     uint32_t interface_index,
    321     uint32_t interval_ms,
    322     int32_t rssi_threshold,
    323     bool request_random_mac,
    324     const std::vector<std::vector<uint8_t>>& scan_ssids,
    325     const std::vector<std::vector<uint8_t>>& match_ssids,
    326     const std::vector<uint32_t>& freqs) {
    327   NL80211Packet start_sched_scan(
    328       netlink_manager_->GetFamilyId(),
    329       NL80211_CMD_START_SCHED_SCAN,
    330       netlink_manager_->GetSequenceNumber(),
    331       getpid());
    332   // Force an ACK response upon success.
    333   start_sched_scan.AddFlag(NLM_F_ACK);
    334 
    335   NL80211NestedAttr scan_ssids_attr(NL80211_ATTR_SCAN_SSIDS);
    336   for (size_t i = 0; i < scan_ssids.size(); i++) {
    337     scan_ssids_attr.AddAttribute(NL80211Attr<vector<uint8_t>>(i, scan_ssids[i]));
    338   }
    339   NL80211NestedAttr freqs_attr(NL80211_ATTR_SCAN_FREQUENCIES);
    340   for (size_t i = 0; i < freqs.size(); i++) {
    341     freqs_attr.AddAttribute(NL80211Attr<uint32_t>(i, freqs[i]));
    342   }
    343 
    344   //   Structure of attributes of scheduled scan filters:
    345   // |                                Nested Attribute: id: NL80211_ATTR_SCHED_SCAN_MATCH                           |
    346   // |     Nested Attributed: id: 0       |    Nested Attributed: id: 1         |      Nested Attr: id: 2     | ... |
    347   // | MATCH_SSID  | MATCH_RSSI(optional) | MATCH_SSID  | MACTCH_RSSI(optional) | MATCH_RSSI(optinal, global) | ... |
    348   NL80211NestedAttr scan_match_attr(NL80211_ATTR_SCHED_SCAN_MATCH);
    349   for (size_t i = 0; i < match_ssids.size(); i++) {
    350     NL80211NestedAttr match_group(i);
    351     match_group.AddAttribute(
    352         NL80211Attr<vector<uint8_t>>(NL80211_SCHED_SCAN_MATCH_ATTR_SSID, match_ssids[i]));
    353     match_group.AddAttribute(
    354         NL80211Attr<int32_t>(NL80211_SCHED_SCAN_MATCH_ATTR_RSSI, rssi_threshold));
    355     scan_match_attr.AddAttribute(match_group);
    356   }
    357 
    358   // Append all attributes to the NL80211_CMD_START_SCHED_SCAN packet.
    359   start_sched_scan.AddAttribute(
    360       NL80211Attr<uint32_t>(NL80211_ATTR_IFINDEX, interface_index));
    361   start_sched_scan.AddAttribute(scan_ssids_attr);
    362   // An absence of NL80211_ATTR_SCAN_FREQUENCIES attribue informs kernel to
    363   // scan all supported frequencies.
    364   if (!freqs.empty()) {
    365     start_sched_scan.AddAttribute(freqs_attr);
    366   }
    367   start_sched_scan.AddAttribute(
    368       NL80211Attr<uint32_t>(NL80211_ATTR_SCHED_SCAN_INTERVAL, interval_ms));
    369   start_sched_scan.AddAttribute(scan_match_attr);
    370   if (request_random_mac) {
    371     start_sched_scan.AddAttribute(
    372         NL80211Attr<uint32_t>(NL80211_ATTR_SCAN_FLAGS,
    373                               NL80211_SCAN_FLAG_RANDOM_ADDR));
    374   }
    375 
    376   vector<unique_ptr<const NL80211Packet>> response;
    377   if (!netlink_manager_->SendMessageAndGetAck(start_sched_scan)) {
    378     LOG(ERROR) << "NL80211_CMD_START_SCHED_SCAN failed";
    379     return false;
    380   }
    381 
    382   return true;
    383 }
    384 
    385 }  // namespace wificond
    386 }  // namespace android
    387