Home | History | Annotate | Download | only in apmanager
      1 //
      2 // Copyright (C) 2015 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 "apmanager/hostapd_monitor.h"
     18 
     19 #include <sys/socket.h>
     20 #include <sys/stat.h>
     21 #include <sys/un.h>
     22 
     23 #include <base/bind.h>
     24 #include <base/logging.h>
     25 #include <base/strings/stringprintf.h>
     26 #include <shill/net/io_handler_factory_container.h>
     27 #include <shill/net/sockets.h>
     28 
     29 using base::Bind;
     30 using base::Unretained;
     31 using shill::IOHandlerFactoryContainer;
     32 using std::string;
     33 
     34 namespace apmanager {
     35 
     36 // static.
     37 #if !defined(__ANDROID__)
     38 const char HostapdMonitor::kLocalPathFormat[] =
     39     "/var/run/apmanager/hostapd/hostapd_ctrl_%s";
     40 #else
     41 const char HostapdMonitor::kLocalPathFormat[] =
     42     "/data/misc/apmanager/hostapd/hostapd_ctrl_%s";
     43 #endif  // __ANDROID__
     44 
     45 const char HostapdMonitor::kHostapdCmdAttach[] = "ATTACH";
     46 const char HostapdMonitor::kHostapdRespOk[] = "OK\n";
     47 const char HostapdMonitor::kHostapdEventStationConnected[] = "AP-STA-CONNECTED";
     48 const char HostapdMonitor::kHostapdEventStationDisconnected[] =
     49     "AP-STA-DISCONNECTED";
     50 const int HostapdMonitor::kHostapdCtrlIfaceCheckIntervalMs = 500;
     51 const int HostapdMonitor::kHostapdCtrlIfaceCheckMaxAttempts = 5;
     52 const int HostapdMonitor::kHostapdAttachTimeoutMs = 1000;
     53 const int HostapdMonitor::kInvalidSocket = -1;
     54 
     55 HostapdMonitor::HostapdMonitor(const EventCallback& callback,
     56                                const string& control_interface_path,
     57                                const string& network_interface_name)
     58     : sockets_(new shill::Sockets()),
     59       event_callback_(callback),
     60       dest_path_(base::StringPrintf("%s/%s",
     61                                     control_interface_path.c_str(),
     62                                     network_interface_name.c_str())),
     63       local_path_(base::StringPrintf(kLocalPathFormat,
     64                                      network_interface_name.c_str())),
     65       hostapd_socket_(kInvalidSocket),
     66       io_handler_factory_(
     67           IOHandlerFactoryContainer::GetInstance()->GetIOHandlerFactory()),
     68       event_dispatcher_(EventDispatcher::GetInstance()),
     69       weak_ptr_factory_(this),
     70       started_(false) {}
     71 
     72 HostapdMonitor::~HostapdMonitor() {
     73   if (hostapd_socket_ != kInvalidSocket) {
     74     unlink(local_path_.c_str());
     75     sockets_->Close(hostapd_socket_);
     76   }
     77 }
     78 
     79 void HostapdMonitor::Start() {
     80   if (started_) {
     81     LOG(ERROR) << "HostapdMonitor already started";
     82     return;
     83   }
     84 
     85   hostapd_ctrl_iface_check_count_ = 0;
     86   // Start off by checking the control interface file for the hostapd process.
     87   event_dispatcher_->PostTask(
     88       Bind(&HostapdMonitor::HostapdCtrlIfaceCheckTask,
     89            weak_ptr_factory_.GetWeakPtr()));
     90   started_ = true;
     91 }
     92 
     93 void HostapdMonitor::HostapdCtrlIfaceCheckTask() {
     94   struct stat buf;
     95   if (stat(dest_path_.c_str(), &buf) != 0) {
     96     if (hostapd_ctrl_iface_check_count_ >= kHostapdCtrlIfaceCheckMaxAttempts) {
     97       // This indicates the hostapd failed to start. Invoke callback indicating
     98       // hostapd start failed.
     99       LOG(ERROR) << "Timeout waiting for hostapd control interface";
    100       event_callback_.Run(kHostapdFailed, "");
    101     } else {
    102       hostapd_ctrl_iface_check_count_++;
    103       event_dispatcher_->PostDelayedTask(
    104           base::Bind(&HostapdMonitor::HostapdCtrlIfaceCheckTask,
    105                      weak_ptr_factory_.GetWeakPtr()),
    106           kHostapdCtrlIfaceCheckIntervalMs);
    107     }
    108     return;
    109   }
    110 
    111   // Control interface is up, meaning hostapd started successfully.
    112   event_callback_.Run(kHostapdStarted, "");
    113 
    114   // Attach to the control interface to receive unsolicited event notifications.
    115   AttachToHostapd();
    116 }
    117 
    118 void HostapdMonitor::AttachToHostapd() {
    119   if (hostapd_socket_ != kInvalidSocket) {
    120     LOG(ERROR) << "Socket already initialized";
    121     return;
    122   }
    123 
    124   // Setup socket address for local file and remote file.
    125   struct sockaddr_un local;
    126   local.sun_family = AF_UNIX;
    127   snprintf(local.sun_path, sizeof(local.sun_path), "%s", local_path_.c_str());
    128   struct sockaddr_un dest;
    129   dest.sun_family = AF_UNIX;
    130   snprintf(dest.sun_path, sizeof(dest.sun_path), "%s", dest_path_.c_str());
    131 
    132   // Setup socket for interprocess communication.
    133   hostapd_socket_ = sockets_->Socket(PF_UNIX, SOCK_DGRAM, 0);
    134   if (hostapd_socket_ < 0) {
    135     LOG(ERROR) << "Failed to open hostapd socket";
    136     return;
    137   }
    138   if (sockets_->Bind(hostapd_socket_,
    139                      reinterpret_cast<struct sockaddr*>(&local),
    140                      sizeof(local)) < 0) {
    141     PLOG(ERROR) << "Failed to bind to local socket";
    142     return;
    143   }
    144   if (sockets_->Connect(hostapd_socket_,
    145                         reinterpret_cast<struct sockaddr*>(&dest),
    146                         sizeof(dest)) < 0) {
    147     PLOG(ERROR) << "Failed to connect";
    148     return;
    149   }
    150 
    151   // Setup IO Input handler.
    152   hostapd_input_handler_.reset(io_handler_factory_->CreateIOInputHandler(
    153       hostapd_socket_,
    154       Bind(&HostapdMonitor::ParseMessage, Unretained(this)),
    155       Bind(&HostapdMonitor::OnReadError, Unretained(this))));
    156 
    157   if (!SendMessage(kHostapdCmdAttach, strlen(kHostapdCmdAttach))) {
    158     LOG(ERROR) << "Failed to attach to hostapd";
    159     return;
    160   }
    161 
    162   // Start a timer for ATTACH response.
    163   attach_timeout_callback_.Reset(
    164       Bind(&HostapdMonitor::AttachTimeoutHandler,
    165            weak_ptr_factory_.GetWeakPtr()));
    166   event_dispatcher_->PostDelayedTask(attach_timeout_callback_.callback(),
    167                                      kHostapdAttachTimeoutMs);
    168   return;
    169 }
    170 
    171 void HostapdMonitor::AttachTimeoutHandler() {
    172   LOG(ERROR) << "Timeout waiting for attach response";
    173 }
    174 
    175 // Method for sending message to hostapd control interface.
    176 bool HostapdMonitor::SendMessage(const char* message, size_t length) {
    177   if (sockets_->Send(hostapd_socket_, message, length, 0) < 0) {
    178     PLOG(ERROR) << "Send to hostapd failed";
    179     return false;
    180   }
    181 
    182   return true;
    183 }
    184 
    185 void HostapdMonitor::ParseMessage(shill::InputData* data) {
    186   string str(reinterpret_cast<const char*>(data->buf), data->len);
    187   // "OK" response for the "ATTACH" command.
    188   if (str == kHostapdRespOk) {
    189     attach_timeout_callback_.Cancel();
    190     return;
    191   }
    192 
    193   // Event messages are in format of <[Level]>[Event] [Detail message].
    194   // For example: <2>AP-STA-CONNECTED 00:11:22:33:44:55
    195   // Refer to wpa_ctrl.h for complete list of possible events.
    196   if (str.find_first_of('<', 0) == 0 && str.find_first_of('>', 0) == 2) {
    197     // Remove the log level.
    198     string msg = str.substr(3);
    199     string event;
    200     string data;
    201     size_t pos = msg.find_first_of(' ', 0);
    202     if (pos == string::npos) {
    203       event = msg;
    204     } else {
    205       event = msg.substr(0, pos);
    206       data = msg.substr(pos + 1);
    207     }
    208 
    209     Event event_code;
    210     if (event == kHostapdEventStationConnected) {
    211       event_code = kStationConnected;
    212     } else if (event == kHostapdEventStationDisconnected) {
    213       event_code = kStationDisconnected;
    214     } else {
    215       LOG(INFO) << "Received unknown event: " << event;
    216       return;
    217     }
    218     event_callback_.Run(event_code, data);
    219     return;
    220   }
    221 
    222   LOG(INFO) << "Received unknown message: " << str;
    223 }
    224 
    225 void HostapdMonitor::OnReadError(const string& error_msg) {
    226   LOG(FATAL) << "Hostapd Socket read returns error: "
    227              << error_msg;
    228 }
    229 
    230 }  // namespace apmanager
    231