普通文本  |  231行  |  7.52 KB

//
// 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