diff options
Diffstat (limited to 'hostapd_monitor.cc')
-rw-r--r-- | hostapd_monitor.cc | 212 |
1 files changed, 212 insertions, 0 deletions
diff --git a/hostapd_monitor.cc b/hostapd_monitor.cc new file mode 100644 index 0000000..e78febc --- /dev/null +++ b/hostapd_monitor.cc @@ -0,0 +1,212 @@ +// Copyright 2015 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "apmanager/hostapd_monitor.h" + +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/un.h> + +#include <base/bind.h> +#include <base/logging.h> +#include <base/strings/stringprintf.h> +#include <shill/net/io_handler_factory_container.h> +#include <shill/net/sockets.h> + +using base::Bind; +using base::Unretained; +using shill::IOHandlerFactoryContainer; +using std::string; + +namespace apmanager { + +// static. +const char HostapdMonitor::kLocalPathFormat[] = + "/var/run/apmanager/hostapd/hostapd_ctrl_%s"; +const char HostapdMonitor::kHostapdCmdAttach[] = "ATTACH"; +const char HostapdMonitor::kHostapdRespOk[] = "OK\n"; +const char HostapdMonitor::kHostapdEventStationConnected[] = "AP-STA-CONNECTED"; +const char HostapdMonitor::kHostapdEventStationDisconnected[] = + "AP-STA-DISCONNECTED"; +const int HostapdMonitor::kHostapdCtrlIfaceCheckIntervalMs = 500; +const int HostapdMonitor::kHostapdCtrlIfaceCheckMaxAttempts = 5; +const int HostapdMonitor::kHostapdAttachTimeoutMs = 1000; +const int HostapdMonitor::kInvalidSocket = -1; + +HostapdMonitor::HostapdMonitor(const EventCallback& callback, + const string& control_interface_path, + const string& network_interface_name) + : sockets_(new shill::Sockets()), + event_callback_(callback), + dest_path_(base::StringPrintf("%s/%s", + control_interface_path.c_str(), + network_interface_name.c_str())), + local_path_(base::StringPrintf(kLocalPathFormat, + network_interface_name.c_str())), + hostapd_socket_(kInvalidSocket), + io_handler_factory_( + IOHandlerFactoryContainer::GetInstance()->GetIOHandlerFactory()), + event_dispatcher_(EventDispatcher::GetInstance()), + weak_ptr_factory_(this), + started_(false) {} + +HostapdMonitor::~HostapdMonitor() { + if (hostapd_socket_ != kInvalidSocket) { + unlink(local_path_.c_str()); + sockets_->Close(hostapd_socket_); + } +} + +void HostapdMonitor::Start() { + if (started_) { + LOG(ERROR) << "HostapdMonitor already started"; + return; + } + + hostapd_ctrl_iface_check_count_ = 0; + // Start off by checking the control interface file for the hostapd process. + event_dispatcher_->PostTask( + Bind(&HostapdMonitor::HostapdCtrlIfaceCheckTask, + weak_ptr_factory_.GetWeakPtr())); + started_ = true; +} + +void HostapdMonitor::HostapdCtrlIfaceCheckTask() { + struct stat buf; + if (stat(dest_path_.c_str(), &buf) != 0) { + if (hostapd_ctrl_iface_check_count_ >= kHostapdCtrlIfaceCheckMaxAttempts) { + // This indicates the hostapd failed to start. Invoke callback indicating + // hostapd start failed. + LOG(ERROR) << "Timeout waiting for hostapd control interface"; + event_callback_.Run(kHostapdFailed, ""); + } else { + hostapd_ctrl_iface_check_count_++; + event_dispatcher_->PostDelayedTask( + base::Bind(&HostapdMonitor::HostapdCtrlIfaceCheckTask, + weak_ptr_factory_.GetWeakPtr()), + kHostapdCtrlIfaceCheckIntervalMs); + } + return; + } + + // Control interface is up, meaning hostapd started successfully. + event_callback_.Run(kHostapdStarted, ""); + + // Attach to the control interface to receive unsolicited event notifications. + AttachToHostapd(); +} + +void HostapdMonitor::AttachToHostapd() { + if (hostapd_socket_ != kInvalidSocket) { + LOG(ERROR) << "Socket already initialized"; + return; + } + + // Setup socket address for local file and remote file. + struct sockaddr_un local; + local.sun_family = AF_UNIX; + snprintf(local.sun_path, sizeof(local.sun_path), "%s", local_path_.c_str()); + struct sockaddr_un dest; + dest.sun_family = AF_UNIX; + snprintf(dest.sun_path, sizeof(dest.sun_path), "%s", dest_path_.c_str()); + + // Setup socket for interprocess communication. + hostapd_socket_ = sockets_->Socket(PF_UNIX, SOCK_DGRAM, 0); + if (hostapd_socket_ < 0) { + LOG(ERROR) << "Failed to open hostapd socket"; + return; + } + if (sockets_->Bind(hostapd_socket_, + reinterpret_cast<struct sockaddr*>(&local), + sizeof(local)) < 0) { + PLOG(ERROR) << "Failed to bind to local socket"; + return; + } + if (sockets_->Connect(hostapd_socket_, + reinterpret_cast<struct sockaddr*>(&dest), + sizeof(dest)) < 0) { + PLOG(ERROR) << "Failed to connect"; + return; + } + + // Setup IO Input handler. + hostapd_input_handler_.reset(io_handler_factory_->CreateIOInputHandler( + hostapd_socket_, + Bind(&HostapdMonitor::ParseMessage, Unretained(this)), + Bind(&HostapdMonitor::OnReadError, Unretained(this)))); + + if (!SendMessage(kHostapdCmdAttach, strlen(kHostapdCmdAttach))) { + LOG(ERROR) << "Failed to attach to hostapd"; + return; + } + + // Start a timer for ATTACH response. + attach_timeout_callback_.Reset( + Bind(&HostapdMonitor::AttachTimeoutHandler, + weak_ptr_factory_.GetWeakPtr())); + event_dispatcher_->PostDelayedTask(attach_timeout_callback_.callback(), + kHostapdAttachTimeoutMs); + return; +} + +void HostapdMonitor::AttachTimeoutHandler() { + LOG(ERROR) << "Timeout waiting for attach response"; +} + +// Method for sending message to hostapd control interface. +bool HostapdMonitor::SendMessage(const char* message, size_t length) { + if (sockets_->Send(hostapd_socket_, message, length, 0) < 0) { + PLOG(ERROR) << "Send to hostapd failed"; + return false; + } + + return true; +} + +void HostapdMonitor::ParseMessage(shill::InputData* data) { + string str(reinterpret_cast<const char*>(data->buf), data->len); + // "OK" response for the "ATTACH" command. + if (str == kHostapdRespOk) { + attach_timeout_callback_.Cancel(); + return; + } + + // Event messages are in format of <[Level]>[Event] [Detail message]. + // For example: <2>AP-STA-CONNECTED 00:11:22:33:44:55 + // Refer to wpa_ctrl.h for complete list of possible events. + if (str.find_first_of('<', 0) == 0 && str.find_first_of('>', 0) == 2) { + // Remove the log level. + string msg = str.substr(3); + string event; + string data; + size_t pos = msg.find_first_of(' ', 0); + if (pos == string::npos) { + event = msg; + } else { + event = msg.substr(0, pos); + data = msg.substr(pos + 1); + } + + Event event_code; + if (event == kHostapdEventStationConnected) { + event_code = kStationConnected; + } else if (event == kHostapdEventStationDisconnected) { + event_code = kStationDisconnected; + } else { + LOG(INFO) << "Received unknown event: " << event; + return; + } + event_callback_.Run(event_code, data); + return; + } + + LOG(INFO) << "Received unknown message: " << str; +} + +void HostapdMonitor::OnReadError(const string& error_msg) { + LOG(FATAL) << "Hostapd Socket read returns error: " + << error_msg; +} + +} // namespace apmanager |