230 lines
7.5 KiB
C++
230 lines
7.5 KiB
C++
//
|
|
// Copyright (C) 2015 The Android Open Source Project
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//
|
|
|
|
#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.
|
|
#if !defined(__ANDROID__)
|
|
const char HostapdMonitor::kLocalPathFormat[] =
|
|
"/var/run/apmanager/hostapd/hostapd_ctrl_%s";
|
|
#else
|
|
const char HostapdMonitor::kLocalPathFormat[] =
|
|
"/data/misc/apmanager/hostapd/hostapd_ctrl_%s";
|
|
#endif // __ANDROID__
|
|
|
|
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
|