| // 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 "shill/passive_link_monitor.h" |
| |
| #include <string> |
| |
| #include <base/bind.h> |
| |
| #include "shill/arp_client.h" |
| #include "shill/arp_packet.h" |
| #include "shill/connection.h" |
| #include "shill/event_dispatcher.h" |
| #include "shill/logging.h" |
| #include "shill/net/byte_string.h" |
| |
| using base::Bind; |
| using base::Unretained; |
| using std::string; |
| |
| namespace shill { |
| |
| namespace Logging { |
| static auto kModuleLogScope = ScopeLogger::kLink; |
| static string ObjectID(Connection* c) { return c->interface_name(); } |
| } |
| |
| // static. |
| const int PassiveLinkMonitor::kDefaultMonitorCycles = 40; |
| const int PassiveLinkMonitor::kCyclePeriodMilliseconds = 25000; |
| const int PassiveLinkMonitor::kMinArpRequestsPerCycle = 5; |
| |
| PassiveLinkMonitor::PassiveLinkMonitor(const ConnectionRefPtr& connection, |
| EventDispatcher* dispatcher, |
| const ResultCallback& result_callback) |
| : connection_(connection), |
| dispatcher_(dispatcher), |
| // Connection is not provided when this is used as a mock for testing |
| // purpose. |
| arp_client_( |
| new ArpClient(connection ? connection->interface_index() : 0)), |
| result_callback_(result_callback), |
| num_cycles_to_monitor_(kDefaultMonitorCycles), |
| num_requests_received_(0), |
| num_cycles_passed_(0) { |
| } |
| |
| PassiveLinkMonitor::~PassiveLinkMonitor() { |
| Stop(); |
| } |
| |
| bool PassiveLinkMonitor::Start(int num_cycles) { |
| SLOG(connection_.get(), 2) << "In " << __func__ << "."; |
| Stop(); |
| |
| if (!StartArpClient()) { |
| return false; |
| } |
| // Start the monitor cycle. |
| monitor_cycle_timeout_callback_.Reset( |
| Bind(&PassiveLinkMonitor::CycleTimeoutHandler, Unretained(this))); |
| dispatcher_->PostDelayedTask(monitor_cycle_timeout_callback_.callback(), |
| kCyclePeriodMilliseconds); |
| num_cycles_to_monitor_ = num_cycles; |
| return true; |
| } |
| |
| void PassiveLinkMonitor::Stop() { |
| SLOG(connection_.get(), 2) << "In " << __func__ << "."; |
| StopArpClient(); |
| num_requests_received_ = 0; |
| num_cycles_passed_ = 0; |
| monitor_cycle_timeout_callback_.Cancel(); |
| monitor_completed_callback_.Cancel(); |
| } |
| |
| bool PassiveLinkMonitor::StartArpClient() { |
| if (!arp_client_->StartRequestListener()) { |
| return false; |
| } |
| receive_request_handler_.reset( |
| dispatcher_->CreateReadyHandler( |
| arp_client_->socket(), |
| IOHandler::kModeInput, |
| Bind(&PassiveLinkMonitor::ReceiveRequest, Unretained(this)))); |
| return true; |
| } |
| |
| void PassiveLinkMonitor::StopArpClient() { |
| arp_client_->Stop(); |
| receive_request_handler_.reset(); |
| } |
| |
| void PassiveLinkMonitor::ReceiveRequest(int fd) { |
| SLOG(connection_.get(), 2) << "In " << __func__ << "."; |
| ArpPacket packet; |
| ByteString sender; |
| |
| if (!arp_client_->ReceivePacket(&packet, &sender)) { |
| return; |
| } |
| |
| if (packet.IsReply()) { |
| SLOG(connection_.get(), 4) << "This is not a request packet. Ignoring."; |
| return; |
| } |
| |
| num_requests_received_++; |
| // Stop ARP client if we receive enough requests for the current cycle. |
| if (num_requests_received_ >= kMinArpRequestsPerCycle) { |
| StopArpClient(); |
| } |
| } |
| |
| void PassiveLinkMonitor::CycleTimeoutHandler() { |
| bool status = false; |
| if (num_requests_received_ >= kMinArpRequestsPerCycle) { |
| num_requests_received_ = 0; |
| num_cycles_passed_++; |
| if (num_cycles_passed_ < num_cycles_to_monitor_) { |
| // Continue on with the next cycle. |
| StartArpClient(); |
| dispatcher_->PostDelayedTask(monitor_cycle_timeout_callback_.callback(), |
| kCyclePeriodMilliseconds); |
| return; |
| } |
| // Monitor completed. |
| status = true; |
| } |
| |
| // Post a task to perform cleanup and invoke result callback, since this |
| // function is invoked from the callback that will be cancelled during |
| // cleanup. |
| monitor_completed_callback_.Reset( |
| Bind(&PassiveLinkMonitor::MonitorCompleted, Unretained(this), status)); |
| dispatcher_->PostTask(monitor_completed_callback_.callback()); |
| } |
| |
| void PassiveLinkMonitor::MonitorCompleted(bool status) { |
| // Stop the monitoring before invoking result callback, so that the ARP client |
| // is stopped by the time result callback is invoked. |
| Stop(); |
| result_callback_.Run(status); |
| } |
| |
| } // namespace shill |