summaryrefslogtreecommitdiff
path: root/hostapd_monitor.cc
diff options
context:
space:
mode:
Diffstat (limited to 'hostapd_monitor.cc')
-rw-r--r--hostapd_monitor.cc212
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