shill: reduce ARP traffic from LinkMonitor

This is achieved by monitoring the link passively (monitor on ARP requests)
after the link is determined as up via active link monitor (sending
ARP requests and probing for ARP replies). Only switch to active link
monitor when the passive link monitor completes after a preset number
of successful cycles or after a single failed cycle.

ARP traffic will be reduced since the device will not be generating
any ARP requests during passive link monitoring period. This will potentially
delay the detection of link failure up to one passive monitoring cycle
(25 seconds) when the link goes down during passive monitoring.

BUG=chromium:422159
TEST=USE="asan clang" FEATURES=test emerge-$BOARD shill
     Connect DUT to a wireless network, and verify link monitor mode switching
     via debug logs

Change-Id: I4c72a048da18cee7dc5be2cf2b51082cb9ae6716
Reviewed-on: https://chromium-review.googlesource.com/242684
Tested-by: Zeping Qiu <zqiu@chromium.org>
Reviewed-by: Zeping Qiu <zqiu@chromium.org>
Commit-Queue: Zeping Qiu <zqiu@chromium.org>
diff --git a/active_link_monitor.cc b/active_link_monitor.cc
index 85644c0..39d6f92 100644
--- a/active_link_monitor.cc
+++ b/active_link_monitor.cc
@@ -50,7 +50,10 @@
       device_info_(device_info),
       failure_callback_(failure_callback),
       success_callback_(success_callback),
-      arp_client_(new ArpClient(connection->interface_index())),
+      // Connection is not provided when this is used as a mock for testing
+      // purpose.
+      arp_client_(
+          new ArpClient(connection ? connection->interface_index() : 0)),
       test_period_milliseconds_(kDefaultTestPeriodMilliseconds),
       broadcast_failure_count_(0),
       unicast_failure_count_(0),
@@ -68,6 +71,7 @@
 }
 
 bool ActiveLinkMonitor::Start(int test_period) {
+  SLOG(connection_.get(), 2) << "In " << __func__ << ".";
   StopMonitorCycle();
   return StartInternal(test_period);
 }
@@ -309,7 +313,7 @@
   } else if (is_unicast_) {
     destination_mac_address = gateway_mac_address_;
   }
-  LOG(INFO) << "IsGatway " << IsGatewayFound() << " unicast: " << is_unicast_;
+
   ArpPacket request(connection_->local(), connection_->gateway(),
                     local_mac_address_, destination_mac_address);
   if (!arp_client_->TransmitRequest(request)) {
diff --git a/active_link_monitor.h b/active_link_monitor.h
index ac49efb..22f7096 100644
--- a/active_link_monitor.h
+++ b/active_link_monitor.h
@@ -80,9 +80,20 @@
   // gateway via broadcast ARP.
   bool IsGatewayFound() const;
 
-  const ByteString &gateway_mac_address() const {
+  virtual const ByteString &gateway_mac_address() const {
     return gateway_mac_address_;
   }
+  virtual void set_gateway_mac_address(const ByteString &gateway_mac_address) {
+    gateway_mac_address_ = gateway_mac_address;
+  }
+
+  virtual bool gateway_supports_unicast_arp() const {
+    return gateway_supports_unicast_arp_;
+  }
+  virtual void set_gateway_supports_unicast_arp(
+      bool gateway_supports_unicast_arp) {
+    gateway_supports_unicast_arp_ = gateway_supports_unicast_arp;
+  }
 
  private:
   friend class ActiveLinkMonitorTest;
diff --git a/link_monitor.cc b/link_monitor.cc
index 668f0f8..bdc5243 100644
--- a/link_monitor.cc
+++ b/link_monitor.cc
@@ -5,21 +5,16 @@
 #include "shill/link_monitor.h"
 
 #include <string>
-#include <vector>
 
 #include <base/bind.h>
-#include <base/strings/stringprintf.h>
-#include <base/strings/string_util.h>
 
-#include "shill/arp_client.h"
-#include "shill/arp_packet.h"
+#include "shill/active_link_monitor.h"
 #include "shill/connection.h"
 #include "shill/device_info.h"
 #include "shill/event_dispatcher.h"
 #include "shill/logging.h"
-#include "shill/metrics.h"
-#include "shill/net/ip_address.h"
 #include "shill/net/shill_time.h"
+#include "shill/passive_link_monitor.h"
 
 using base::Bind;
 using base::Unretained;
@@ -32,12 +27,11 @@
 static string ObjectID(Connection *c) { return c->interface_name(); }
 }
 
-const int LinkMonitor::kDefaultTestPeriodMilliseconds = 5000;
+const int LinkMonitor::kDefaultTestPeriodMilliseconds =
+    ActiveLinkMonitor::kDefaultTestPeriodMilliseconds;
+const int LinkMonitor::kFailureThreshold =
+    ActiveLinkMonitor::kFailureThreshold;
 const char LinkMonitor::kDefaultLinkMonitorTechnologies[] = "wifi";
-const int LinkMonitor::kFailureThreshold = 5;
-const int LinkMonitor::kFastTestPeriodMilliseconds = 200;
-const int LinkMonitor::kMaxResponseSampleFilterDepth = 5;
-const int LinkMonitor::kUnicastReplyReliabilityThreshold = 10;
 
 LinkMonitor::LinkMonitor(const ConnectionRefPtr &connection,
                          EventDispatcher *dispatcher,
@@ -48,18 +42,24 @@
     : connection_(connection),
       dispatcher_(dispatcher),
       metrics_(metrics),
-      device_info_(device_info),
       failure_callback_(failure_callback),
       gateway_change_callback_(gateway_change_callback),
-      test_period_milliseconds_(kDefaultTestPeriodMilliseconds),
-      broadcast_failure_count_(0),
-      unicast_failure_count_(0),
-      broadcast_success_count_(0),
-      unicast_success_count_(0),
-      is_unicast_(false),
-      gateway_supports_unicast_arp_(false),
-      response_sample_count_(0),
-      response_sample_bucket_(0),
+      active_link_monitor_(
+          new ActiveLinkMonitor(
+              connection,
+              dispatcher,
+              metrics,
+              device_info,
+              Bind(&LinkMonitor::OnActiveLinkMonitorFailure,
+                   Unretained(this)),
+              Bind(&LinkMonitor::OnActiveLinkMonitorSuccess,
+                   Unretained(this)))),
+      passive_link_monitor_(
+          new PassiveLinkMonitor(
+              connection,
+              dispatcher,
+              Bind(&LinkMonitor::OnPassiveLinkMonitorResultCallback,
+                   Unretained(this)))),
       time_(Time::GetInstance()) {
 }
 
@@ -69,281 +69,80 @@
 
 bool LinkMonitor::Start() {
   Stop();
-  return StartInternal(kDefaultTestPeriodMilliseconds);
-}
-
-bool LinkMonitor::StartInternal(int probe_period_milliseconds) {
-  test_period_milliseconds_ = probe_period_milliseconds;
-  if (test_period_milliseconds_ > kDefaultTestPeriodMilliseconds) {
-    LOG(WARNING) << "Long test period; UMA stats will be truncated.";
-  }
-
-  if (!device_info_->GetMACAddress(
-          connection_->interface_index(), &local_mac_address_)) {
-    LOG(ERROR) << "Could not get local MAC address.";
-    metrics_->NotifyLinkMonitorFailure(
-        connection_->technology(),
-        Metrics::kLinkMonitorMacAddressNotFound,
-        0, 0, 0);
-    Stop();
-    return false;
-  }
-  if (gateway_mac_address_.IsEmpty()) {
-    gateway_mac_address_ = ByteString(local_mac_address_.GetLength());
-  }
-  send_request_callback_.Reset(
-      Bind(base::IgnoreResult(&LinkMonitor::SendRequest), Unretained(this)));
   time_->GetTimeMonotonic(&started_monitoring_at_);
-  return SendRequest();
+  // Start active link monitor.
+  return active_link_monitor_->Start(
+      ActiveLinkMonitor::kDefaultTestPeriodMilliseconds);
 }
 
 void LinkMonitor::Stop() {
   SLOG(connection_.get(), 2) << "In " << __func__ << ".";
-  local_mac_address_.Clear();
-  gateway_mac_address_.Clear();
-  arp_client_.reset();
-  broadcast_failure_count_ = 0;
-  unicast_failure_count_ = 0;
-  broadcast_success_count_ = 0;
-  unicast_success_count_ = 0;
-  is_unicast_ = false;
-  gateway_supports_unicast_arp_ = false;
-  response_sample_bucket_ = 0;
-  response_sample_count_ = 0;
-  receive_response_handler_.reset();
-  send_request_callback_.Cancel();
   timerclear(&started_monitoring_at_);
-  timerclear(&sent_request_at_);
+  active_link_monitor_->Stop();
+  passive_link_monitor_->Stop();
+  gateway_mac_address_.Clear();
 }
 
 void LinkMonitor::OnAfterResume() {
+  // Preserve gateway settings across resume.
   ByteString prior_gateway_mac_address(gateway_mac_address_);
-  bool gateway_supports_unicast_arp = gateway_supports_unicast_arp_;
+  bool gateway_supports_unicast_arp =
+      active_link_monitor_->gateway_supports_unicast_arp();
   Stop();
   gateway_mac_address_ = prior_gateway_mac_address;
-  gateway_supports_unicast_arp_ = gateway_supports_unicast_arp;
-  StartInternal(kFastTestPeriodMilliseconds);
+  active_link_monitor_->set_gateway_mac_address(gateway_mac_address_);
+  active_link_monitor_->set_gateway_supports_unicast_arp(
+      gateway_supports_unicast_arp);
+
+  active_link_monitor_->Start(ActiveLinkMonitor::kFastTestPeriodMilliseconds);
 }
 
 int LinkMonitor::GetResponseTimeMilliseconds() const {
-  return response_sample_count_ ?
-      response_sample_bucket_ / response_sample_count_ : 0;
-}
-
-void LinkMonitor::AddResponseTimeSample(int response_time_milliseconds) {
-  SLOG(connection_.get(), 2) << "In " << __func__ << " with sample "
-                             << response_time_milliseconds << ".";
-  metrics_->NotifyLinkMonitorResponseTimeSampleAdded(
-      connection_->technology(), response_time_milliseconds);
-  response_sample_bucket_ += response_time_milliseconds;
-  if (response_sample_count_ < kMaxResponseSampleFilterDepth) {
-    ++response_sample_count_;
-  } else {
-    response_sample_bucket_ =
-        response_sample_bucket_ * kMaxResponseSampleFilterDepth /
-            (kMaxResponseSampleFilterDepth + 1);
-  }
-}
-
-// static
-string LinkMonitor::HardwareAddressToString(const ByteString &address) {
-  std::vector<string> address_parts;
-  for (size_t i = 0; i < address.GetLength(); ++i) {
-    address_parts.push_back(
-        base::StringPrintf("%02x", address.GetConstData()[i]));
-  }
-  return JoinString(address_parts, ':');
-}
-
-bool LinkMonitor::CreateClient() {
-  arp_client_.reset(new ArpClient(connection_->interface_index()));
-
-  if (!arp_client_->StartReplyListener()) {
-    return false;
-  }
-  SLOG(connection_.get(), 4) << "Created ARP client; listening on socket "
-                             << arp_client_->socket() << ".";
-  receive_response_handler_.reset(
-    dispatcher_->CreateReadyHandler(
-        arp_client_->socket(),
-        IOHandler::kModeInput,
-        Bind(&LinkMonitor::ReceiveResponse, Unretained(this))));
-  return true;
-}
-
-bool LinkMonitor::AddMissedResponse() {
-  SLOG(connection_.get(), 2) << "In " << __func__ << ".";
-  AddResponseTimeSample(test_period_milliseconds_);
-
-  if (is_unicast_) {
-    if (gateway_supports_unicast_arp_) {
-      ++unicast_failure_count_;
-    }
-    unicast_success_count_ = 0;
-  } else {
-    ++broadcast_failure_count_;
-    broadcast_success_count_ = 0;
-  }
-
-  if (unicast_failure_count_ + broadcast_failure_count_ >= kFailureThreshold) {
-    LOG(ERROR) << "Link monitor has reached the failure threshold with "
-               << broadcast_failure_count_
-               << " broadcast failures and "
-               << unicast_failure_count_
-               << " unicast failures.";
-    failure_callback_.Run();
-
-    struct timeval now, elapsed_time;
-    time_->GetTimeMonotonic(&now);
-    timersub(&now, &started_monitoring_at_, &elapsed_time);
-
-    metrics_->NotifyLinkMonitorFailure(
-        connection_->technology(),
-        Metrics::kLinkMonitorFailureThresholdReached,
-        elapsed_time.tv_sec,
-        broadcast_failure_count_,
-        unicast_failure_count_);
-
-    Stop();
-    return true;
-  }
-  is_unicast_ = !is_unicast_;
-  return false;
+  return active_link_monitor_->GetResponseTimeMilliseconds();
 }
 
 bool LinkMonitor::IsGatewayFound() const {
   return !gateway_mac_address_.IsZero();
 }
 
-void LinkMonitor::ReceiveResponse(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 reply packet.  Ignoring.";
-    return;
-  }
-
-  if (!connection_->local().address().Equals(
-           packet.remote_ip_address().address())) {
-    SLOG(connection_.get(), 4) << "Response is not for our IP address.";
-    return;
-  }
-
-  if (!local_mac_address_.Equals(packet.remote_mac_address())) {
-    SLOG(connection_.get(), 4) << "Response is not for our MAC address.";
-    return;
-  }
-
-  if (!connection_->gateway().address().Equals(
-           packet.local_ip_address().address())) {
-    SLOG(connection_.get(), 4)
-        << "Response is not from the gateway IP address.";
-    return;
-  }
+void LinkMonitor::OnActiveLinkMonitorFailure(
+    Metrics::LinkMonitorFailure failure,
+    int broadcast_failure_count,
+    int unicast_failure_count) {
+  failure_callback_.Run();
 
   struct timeval now, elapsed_time;
   time_->GetTimeMonotonic(&now);
-  timersub(&now, &sent_request_at_, &elapsed_time);
+  timersub(&now, &started_monitoring_at_, &elapsed_time);
 
-  AddResponseTimeSample(elapsed_time.tv_sec * 1000 +
-                        elapsed_time.tv_usec / 1000);
+  metrics_->NotifyLinkMonitorFailure(
+      connection_->technology(),
+      failure,
+      elapsed_time.tv_sec,
+      broadcast_failure_count,
+      unicast_failure_count);
 
-  receive_response_handler_.reset();
-  arp_client_.reset();
+  Stop();
+}
 
-  if (is_unicast_) {
-    ++unicast_success_count_;
-    unicast_failure_count_ = 0;
-    if (unicast_success_count_ >= kUnicastReplyReliabilityThreshold) {
-      SLOG_IF(Link, 2, !gateway_supports_unicast_arp_)
-          << "Gateway is now considered a reliable unicast responder.  "
-             "Unicast failures will now count.";
-      gateway_supports_unicast_arp_ = true;
-    }
-  } else {
-    ++broadcast_success_count_;
-    broadcast_failure_count_ = 0;
-  }
-
-  if (!gateway_mac_address_.Equals(packet.local_mac_address())) {
-    const ByteString &new_mac_address = packet.local_mac_address();
-    if (!IsGatewayFound()) {
-      SLOG(connection_.get(), 2) << "Found gateway at "
-                                 << HardwareAddressToString(new_mac_address);
-    } else {
-      SLOG(connection_.get(), 2) << "Gateway MAC address changed.";
-    }
-    gateway_mac_address_ = new_mac_address;
-
+void LinkMonitor::OnActiveLinkMonitorSuccess() {
+  if (!gateway_mac_address_.Equals(
+      active_link_monitor_->gateway_mac_address())) {
+    gateway_mac_address_ = active_link_monitor_->gateway_mac_address();
     // Notify device of the new gateway mac address.
     gateway_change_callback_.Run();
   }
 
-  is_unicast_ = !is_unicast_;
-  if ((unicast_success_count_ || !gateway_supports_unicast_arp_)
-      && broadcast_success_count_) {
-    test_period_milliseconds_ = kDefaultTestPeriodMilliseconds;
-  }
+  // Start passive link monitoring.
+  passive_link_monitor_->Start(PassiveLinkMonitor::kDefaultMonitorCycles);
 }
 
-bool LinkMonitor::SendRequest() {
-  SLOG(connection_.get(), 2) << "In " << __func__ << ".";
-  if (!arp_client_.get()) {
-    if (!CreateClient()) {
-      LOG(ERROR) << "Failed to start ARP client.";
-      Stop();
-      metrics_->NotifyLinkMonitorFailure(
-          connection_->technology(),
-          Metrics::kLinkMonitorClientStartFailure,
-          0, 0, 0);
-      return false;
-    }
-  } else if (AddMissedResponse()) {
-    // If an ARP client is still listening, this means we have timed
-    // out reception of the ARP reply.
-    return false;
-  } else {
-    // We already have an ArpClient instance running.  These aren't
-    // bound sockets in the conventional sense, and we cannot distinguish
-    // which request (from which trial, or even from which component
-    // in the local system) an ARP reply was sent in response to.
-    // Therefore we keep the already open ArpClient in the case of
-    // a non-fatal timeout.
-  }
-  ByteString destination_mac_address(gateway_mac_address_.GetLength());
-  if (!IsGatewayFound()) {
-    // The remote MAC addess is set by convention to be all-zeroes in the
-    // ARP header if not known.  The ArpClient will translate an all-zeroes
-    // remote address into a send to the broadcast (all-ones) address in
-    // the Ethernet frame header.
-    SLOG_IF(Link, 2, is_unicast_) << "Sending broadcast since "
-                                  << "gateway MAC is unknown";
-    is_unicast_ = false;
-  } else if (is_unicast_) {
-    destination_mac_address = gateway_mac_address_;
-  }
+void LinkMonitor::OnPassiveLinkMonitorResultCallback(bool status) {
+  // TODO(zqiu): Add metrics for tracking passive link monitor results.
 
-  ArpPacket request(connection_->local(), connection_->gateway(),
-                    local_mac_address_, destination_mac_address);
-  if (!arp_client_->TransmitRequest(request)) {
-    LOG(ERROR) << "Failed to send ARP request.  Stopping.";
-    Stop();
-    metrics_->NotifyLinkMonitorFailure(
-        connection_->technology(), Metrics::kLinkMonitorTransmitFailure,
-        0, 0, 0);
-    return false;
-  }
-
-  time_->GetTimeMonotonic(&sent_request_at_);
-
-  dispatcher_->PostDelayedTask(send_request_callback_.callback(),
-                               test_period_milliseconds_);
-  return true;
+  // Start active monitor
+  active_link_monitor_->Start(
+      ActiveLinkMonitor::kDefaultTestPeriodMilliseconds);
 }
 
 }  // namespace shill
diff --git a/link_monitor.h b/link_monitor.h
index c04889f..17ac3bd 100644
--- a/link_monitor.h
+++ b/link_monitor.h
@@ -13,39 +13,32 @@
 #include <base/callback.h>
 #include <base/cancelable_callback.h>
 
+#include "shill/metrics.h"
 #include "shill/net/byte_string.h"
 #include "shill/refptr_types.h"
 
 namespace shill {
 
-class ArpClient;
+class ActiveLinkMonitor;
 class DeviceInfo;
 class EventDispatcher;
-class IOHandler;
-class Metrics;
+class PassiveLinkMonitor;
 class Time;
 
-// LinkMonitor tracks the status of a connection by sending ARP
-// messages to the default gateway for a connection.  It keeps
-// track of response times which can be an indicator of link
-// quality.  It signals to caller that the link has failed if
-// too many requests go unanswered. It also signals to caller
-// when the gateway's mac address is found or changed.
 class LinkMonitor {
  public:
   typedef base::Closure FailureCallback;
   typedef base::Closure GatewayChangeCallback;
 
-  // The default number of milliseconds between ARP requests. Needed by Metrics.
+  // The default number of milliseconds between ARP requests used by
+  // ActiveLinkMonitor. Needed by Metrics.
   static const int kDefaultTestPeriodMilliseconds;
 
   // The default list of technologies for which link monitoring is enabled.
   // Needed by DefaultProfile.
   static const char kDefaultLinkMonitorTechnologies[];
 
-  // When the sum of consecutive counted unicast and broadcast failures
-  // equals this value, the failure callback is called, the counters
-  // are reset, and the link monitoring quiesces.  Needed by Metrics.
+  // Failure threshold count used by ActiveLinkMonitor.  Needed by Metrics.
   static const int kFailureThreshold;
 
   LinkMonitor(const ConnectionRefPtr &connection,
@@ -64,7 +57,7 @@
   virtual void Stop();
 
   // Inform LinkMonitor that the system is resuming from sleep.
-  // LinkMonitor will immediately probe the gateway, using a lower
+  // LinkMonitor will immediately start the ActiveLinkMonitor, using a lower
   // timeout than normal.
   virtual void OnAfterResume();
 
@@ -83,45 +76,13 @@
   }
 
  private:
-  friend class LinkMonitorForTest;
   friend class LinkMonitorTest;
 
-  // The number of milliseconds between ARP requests when running a quick test.
-  // Needed by unit tests.
-  static const int kFastTestPeriodMilliseconds;
-
-  // The number of samples to compute a "strict" average over.  When
-  // more samples than this number arrive, this determines how "slow"
-  // our simple low-pass filter works.
-  static const int kMaxResponseSampleFilterDepth;
-
-  // When the sum of consecutive unicast successes equals this value,
-  // we can assume that in general this gateway supports unicast ARP
-  // requests, and we will count future unicast failures.
-  static const int kUnicastReplyReliabilityThreshold;
-
-  // Similar to Start, except that the initial probes use
-  // |probe_period_milliseconds|. After successfully probing with both
-  // broadcast and unicast ARPs (at least one of each), LinkMonitor
-  // switches itself to kDefaultTestPeriodMilliseconds.
-  virtual bool StartInternal(int probe_period_milliseconds);
-  // Add a response time sample to the buffer.
-  void AddResponseTimeSample(int response_time_milliseconds);
-  // Create an ArpClient instance so we can receive and transmit ARP
-  // packets.  This method is virtual so it can be overridden in
-  // unit tests.
-  virtual bool CreateClient();
-  // Convert a hardware address byte-string to a colon-separated string.
-  static std::string HardwareAddressToString(const ByteString &address);
-  // Denote a missed response.  Returns true if this loss has caused us
-  // to exceed the failure threshold.
-  bool AddMissedResponse();
-  // This I/O callback is triggered whenever the ARP reception socket
-  // has data available to be received.
-  void ReceiveResponse(int fd);
-  // Send the next ARP request.  Returns true if successful, false
-  // otherwise.
-  bool SendRequest();
+  void OnActiveLinkMonitorFailure(Metrics::LinkMonitorFailure failure,
+                                  int broadcast_failure_count,
+                                  int unicast_failure_count);
+  void OnActiveLinkMonitorSuccess();
+  void OnPassiveLinkMonitorResultCallback(bool status);
 
   // The connection on which to perform link monitoring.
   ConnectionRefPtr connection_;
@@ -129,63 +90,16 @@
   EventDispatcher *dispatcher_;
   // Metrics instance on which to post performance results.
   Metrics *metrics_;
-  // DeviceInfo instance for retrieving the MAC address of a device.
-  DeviceInfo *device_info_;
   // Failure callback method to call if LinkMonitor fails.
   FailureCallback failure_callback_;
   // Callback method to call if gateway mac address changes.
   GatewayChangeCallback gateway_change_callback_;
-  // The MAC address of device associated with this connection.
-  ByteString local_mac_address_;
+  std::unique_ptr<ActiveLinkMonitor> active_link_monitor_;
+  std::unique_ptr<PassiveLinkMonitor> passive_link_monitor_;
   // The MAC address of the default gateway.
   ByteString gateway_mac_address_;
-  // ArpClient instance used for performing link tests.
-  std::unique_ptr<ArpClient> arp_client_;
-
-  // How frequently we send an ARP request. This is also the timeout
-  // for a pending request.
-  int test_period_milliseconds_;
-  // The number of consecutive times we have failed in receiving
-  // responses to broadcast ARP requests.
-  int broadcast_failure_count_;
-  // The number of consecutive times we have failed in receiving
-  // responses to unicast ARP requests.
-  int unicast_failure_count_;
-  // The number of consecutive times we have succeeded in receiving
-  // responses to broadcast ARP requests.
-  int broadcast_success_count_;
-  // The number of consecutive times we have succeeded in receiving
-  // responses to unicast ARP requests.
-  int unicast_success_count_;
-
-  // Whether this iteration of the test was a unicast request
-  // to the gateway instead of broadcast.  The link monitor
-  // alternates between unicast and broadcast requests so that
-  // both types of network traffic is monitored.
-  bool is_unicast_;
-
-  // Whether we have observed that the gateway reliably responds
-  // to unicast ARP requests.
-  bool gateway_supports_unicast_arp_;
-
-  // Number of response samples received in our rolling averge.
-  int response_sample_count_;
-  // The sum of response samples in our rolling average.
-  int response_sample_bucket_;
-
-  // IOCallback that fires when the socket associated with our ArpClient
-  // has a packet to be received.  Calls ReceiveResponse().
-  std::unique_ptr<IOHandler> receive_response_handler_;
-  // Callback method used for periodic transmission of ARP requests.
-  // When the timer expires this will call SendRequest() through the
-  // void callback function SendRequestTask().
-  base::CancelableClosure send_request_callback_;
-
   // The time at which the link monitor started.
   struct timeval started_monitoring_at_;
-
-  // The time at which the last ARP request was sent.
-  struct timeval sent_request_at_;
   // Time instance for performing GetTimeMonotonic().
   Time *time_;
 
diff --git a/link_monitor_unittest.cc b/link_monitor_unittest.cc
index 5aad8f5..228be07 100644
--- a/link_monitor_unittest.cc
+++ b/link_monitor_unittest.cc
@@ -4,25 +4,21 @@
 
 #include "shill/link_monitor.h"
 
-#include <net/if_arp.h>
-
 #include <string>
 
 #include <base/bind.h>
 #include <gtest/gtest.h>
 
-#include "shill/arp_packet.h"
 #include "shill/logging.h"
-#include "shill/mock_arp_client.h"
+#include "shill/mock_active_link_monitor.h"
 #include "shill/mock_connection.h"
 #include "shill/mock_control.h"
 #include "shill/mock_device_info.h"
 #include "shill/mock_event_dispatcher.h"
 #include "shill/mock_log.h"
 #include "shill/mock_metrics.h"
+#include "shill/mock_passive_link_monitor.h"
 #include "shill/net/byte_string.h"
-#include "shill/net/ip_address.h"
-#include "shill/net/mock_sockets.h"
 #include "shill/net/mock_time.h"
 
 using base::Bind;
@@ -43,118 +39,64 @@
 namespace shill {
 
 namespace {
-const char kInterfaceName[] = "int0";
-const char kLocalIPAddress[] = "10.0.1.1";
-const uint8_t kLocalMACAddress[] = { 0, 1, 2, 3, 4, 5 };
-const char kRemoteIPAddress[] = "10.0.1.2";
-const uint8_t kRemoteMACAddress[] = { 6, 7, 8, 9, 10, 11 };
-const char kDBusPath[] = "/dbus/path";
+const uint8_t kGatewayMACAddress[] = { 0, 1, 2, 3, 4, 5 };
 }  // namespace
 
-
-class LinkMonitorForTest : public LinkMonitor {
+class LinkMonitorObserver {
  public:
-  LinkMonitorForTest(const ConnectionRefPtr &connection,
-                     EventDispatcher *dispatcher,
-                     Metrics *metrics,
-                     DeviceInfo *device_info)
-      : LinkMonitor(connection, dispatcher, metrics, device_info,
-                    Bind(&LinkMonitorForTest::FailureCallbackHandler,
-                         Unretained(this)),
-                    Bind(&LinkMonitorForTest::GatewayChangeCallbackHandler,
-                         Unretained(this))) {}
+  LinkMonitorObserver()
+      : failure_callback_(
+            Bind(&LinkMonitorObserver::OnFailureCallback, Unretained(this))),
+        gateway_change_callback_(
+            Bind(&LinkMonitorObserver::OnGatewayChangeCallback,
+                 Unretained(this))) {}
+  virtual ~LinkMonitorObserver() {}
 
-  virtual ~LinkMonitorForTest() {}
+  MOCK_METHOD0(OnFailureCallback, void());
+  MOCK_METHOD0(OnGatewayChangeCallback, void());
 
-  MOCK_METHOD0(CreateClient, bool());
+  const LinkMonitor::FailureCallback failure_callback() {
+    return failure_callback_;
+  }
 
-  // This does not override any methods in LinkMonitor, but is used here
-  // in unit tests to support expectations when the LinkMonitor notifies
-  // its client of a failure.
-  MOCK_METHOD0(FailureCallbackHandler, void());
+  const LinkMonitor::GatewayChangeCallback gateway_change_callback() {
+    return gateway_change_callback_;
+  }
 
-  // This does not override any methods in LinkMonitor, but is used here
-  // in unit tests to support expectations when the LinkMonitor notifies
-  // its client of a gateway mac address change.
-  MOCK_METHOD0(GatewayChangeCallbackHandler, void());
+ private:
+  LinkMonitor::FailureCallback failure_callback_;
+  LinkMonitor::GatewayChangeCallback gateway_change_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(LinkMonitorObserver);
 };
 
-MATCHER_P4(IsArpRequest, local_ip, remote_ip, local_mac, remote_mac, "") {
-  if (local_ip.Equals(arg.local_ip_address()) &&
-      remote_ip.Equals(arg.remote_ip_address()) &&
-      local_mac.Equals(arg.local_mac_address()) &&
-      remote_mac.Equals(arg.remote_mac_address()))
-    return true;
-
-  if (!local_ip.Equals(arg.local_ip_address())) {
-    *result_listener << "Local IP '" << arg.local_ip_address().ToString()
-                     << "' (wanted '" << local_ip.ToString() << "').";
-  }
-
-  if (!remote_ip.Equals(arg.remote_ip_address())) {
-    *result_listener << "Remote IP '" << arg.remote_ip_address().ToString()
-                     << "' (wanted '" << remote_ip.ToString() << "').";
-  }
-
-  if (!local_mac.Equals(arg.local_mac_address())) {
-    *result_listener << "Local MAC '" << arg.local_mac_address().HexEncode()
-                     << "' (wanted " << local_mac.HexEncode() << ")'.";
-  }
-
-  if (!remote_mac.Equals(arg.remote_mac_address())) {
-    *result_listener << "Remote MAC '" << arg.remote_mac_address().HexEncode()
-                     << "' (wanted " << remote_mac.HexEncode() << ")'.";
-  }
-
-  return false;
-}
-
 class LinkMonitorTest : public Test {
  public:
   LinkMonitorTest()
       : metrics_(&dispatcher_),
         device_info_(&control_, nullptr, nullptr, nullptr),
         connection_(new StrictMock<MockConnection>(&device_info_)),
-        monitor_(connection_, &dispatcher_, &metrics_, &device_info_),
-        client_(nullptr),
-        next_client_(new StrictMock<MockArpClient>()),
-        gateway_ip_(IPAddress::kFamilyIPv4),
-        local_ip_(IPAddress::kFamilyIPv4),
-        gateway_mac_(kRemoteMACAddress, arraysize(kRemoteMACAddress)),
-        local_mac_(kLocalMACAddress, arraysize(kLocalMACAddress)),
-        zero_mac_(arraysize(kLocalMACAddress)),
-        link_scope_logging_was_enabled_(false),
-        interface_name_(kInterfaceName) {}
+        active_link_monitor_(new MockActiveLinkMonitor()),
+        passive_link_monitor_(new MockPassiveLinkMonitor()),
+        monitor_(connection_,
+                 &dispatcher_,
+                 &metrics_,
+                 &device_info_,
+                 observer_.failure_callback(),
+                 observer_.gateway_change_callback()) {}
   virtual ~LinkMonitorTest() {}
 
   virtual void SetUp() {
-    link_scope_logging_was_enabled_ = SLOG_IS_ON(Link, 0);
-    if (!link_scope_logging_was_enabled_) {
-      ScopeLogger::GetInstance()->EnableScopesByName("link");
-      ScopeLogger::GetInstance()->set_verbose_level(4);
-    }
+    monitor_.active_link_monitor_.reset(active_link_monitor_);
+    monitor_.passive_link_monitor_.reset(passive_link_monitor_);
     monitor_.time_ = &time_;
+
     time_val_.tv_sec = 0;
     time_val_.tv_usec = 0;
     EXPECT_CALL(time_, GetTimeMonotonic(_))
         .WillRepeatedly(DoAll(SetArgumentPointee<0>(time_val_), Return(0)));
-    EXPECT_TRUE(local_ip_.SetAddressFromString(kLocalIPAddress));
-    EXPECT_CALL(*connection_, local()).WillRepeatedly(ReturnRef(local_ip_));
-    EXPECT_TRUE(gateway_ip_.SetAddressFromString(kRemoteIPAddress));
-    EXPECT_CALL(*connection_, gateway()).WillRepeatedly(ReturnRef(gateway_ip_));
     EXPECT_CALL(*connection_, technology())
         .WillRepeatedly(Return(Technology::kEthernet));
-    EXPECT_CALL(*connection_, ipconfig_rpc_identifier())
-        .WillRepeatedly(testing::ReturnPointee(&kDBusPath));
-    EXPECT_CALL(*connection_, interface_name())
-        .WillRepeatedly(ReturnRef(interface_name_));
-  }
-
-  virtual void TearDown() {
-    if (!link_scope_logging_was_enabled_) {
-      ScopeLogger::GetInstance()->EnableScopesByName("-link");
-      ScopeLogger::GetInstance()->set_verbose_level(0);
-    }
   }
 
   void AdvanceTime(int time_ms) {
@@ -166,667 +108,155 @@
         .WillRepeatedly(DoAll(SetArgumentPointee<0>(time_val_), Return(0)));
   }
 
-  bool CreateMockClient() {
-    EXPECT_FALSE(monitor_.arp_client_.get());
-    if (client_) {
-      Mock::VerifyAndClearExpectations(client_);
-    }
-    client_ = next_client_;
-    next_client_ = new StrictMock<MockArpClient>();
-    monitor_.arp_client_.reset(client_);
-    return true;
+  void SetGatewayMacAddress(const ByteString &gateway_mac_address) {
+    monitor_.gateway_mac_address_ = gateway_mac_address;
   }
 
-  string HardwareAddressToString(const ByteString &address) {
-    return LinkMonitor::HardwareAddressToString(address);
+  void VerifyGatewayMacAddress(const ByteString &gateway_mac_address) {
+    EXPECT_TRUE(monitor_.gateway_mac_address_.Equals(gateway_mac_address));
+  }
+
+  void TriggerActiveLinkMonitorFailure(Metrics::LinkMonitorFailure failure,
+                                       int broadcast_failure_count,
+                                       int unicast_failure_count) {
+    monitor_.OnActiveLinkMonitorFailure(failure,
+                                    broadcast_failure_count,
+                                    unicast_failure_count);
+  }
+
+  void TriggerActiveLinkMonitorSuccess() {
+    monitor_.OnActiveLinkMonitorSuccess();
+  }
+
+  void TriggerPassiveLinkMonitorResultCallback(bool status) {
+    monitor_.OnPassiveLinkMonitorResultCallback(status);
   }
 
  protected:
-  void ExpectReset() {
-    EXPECT_FALSE(monitor_.GetResponseTimeMilliseconds());
-    EXPECT_FALSE(GetArpClient());
-    EXPECT_TRUE(GetSendRequestCallback().IsCancelled());
-    EXPECT_EQ(0, GetBroadcastFailureCount());
-    EXPECT_EQ(0, GetUnicastFailureCount());
-    EXPECT_EQ(0, GetBroadcastSuccessCount());
-    EXPECT_EQ(0, GetUnicastSuccessCount());
-    EXPECT_FALSE(IsUnicast());
-    EXPECT_FALSE(GatewaySupportsUnicastArp());
-  }
-  const ArpClient *GetArpClient() { return monitor_.arp_client_.get(); }
-  void TriggerRequestTimer() {
-    GetSendRequestCallback().callback().Run();
-  }
-  const base::CancelableClosure &GetSendRequestCallback() {
-    return monitor_.send_request_callback_;
-  }
-  int GetBroadcastFailureCount() {
-    return monitor_.broadcast_failure_count_;
-  }
-  int GetUnicastFailureCount() {
-    return monitor_.unicast_failure_count_;
-  }
-  int GetBroadcastSuccessCount() {
-    return monitor_.broadcast_success_count_;
-  }
-  int GetUnicastSuccessCount() {
-    return monitor_.unicast_success_count_;
-  }
-  bool IsUnicast() { return monitor_.is_unicast_; }
-  bool GatewaySupportsUnicastArp() {
-    return monitor_.gateway_supports_unicast_arp_;
-  }
-  void SetGatewaySupportsUnicastArp(bool gateway_supports_unicast_arp) {
-    monitor_.gateway_supports_unicast_arp_ = gateway_supports_unicast_arp;
-  }
-  int GetCurrentTestPeriodMilliseconds() {
-    return monitor_.test_period_milliseconds_;
-  }
-  int GetDefaultTestPeriodMilliseconds() {
-    return LinkMonitor::kDefaultTestPeriodMilliseconds;
-  }
-  size_t GetFailureThreshold() {
-    return LinkMonitor::kFailureThreshold;
-  }
-  size_t GetUnicastReplyReliabilityThreshold() {
-    return LinkMonitor::kUnicastReplyReliabilityThreshold;
-  }
-  int GetFastTestPeriodMilliseconds() {
-    return LinkMonitor::kFastTestPeriodMilliseconds;
-  }
-  int GetMaxResponseSampleFilterDepth() {
-    return LinkMonitor::kMaxResponseSampleFilterDepth;
-  }
-  void ExpectTransmit(bool is_unicast, int transmit_period_milliseconds) {
-    const ByteString &destination_mac = is_unicast ? gateway_mac_ : zero_mac_;
-    if (monitor_.arp_client_.get()) {
-      EXPECT_EQ(client_, monitor_.arp_client_.get());
-      EXPECT_CALL(*client_, TransmitRequest(
-          IsArpRequest(local_ip_, gateway_ip_, local_mac_, destination_mac)))
-          .WillOnce(Return(true));
-    } else {
-      EXPECT_CALL(monitor_, CreateClient())
-          .WillOnce(Invoke(this, &LinkMonitorTest::CreateMockClient));
-      EXPECT_CALL(*next_client_, TransmitRequest(
-          IsArpRequest(local_ip_, gateway_ip_, local_mac_, destination_mac)))
-          .WillOnce(Return(true));
-    }
-    EXPECT_CALL(dispatcher_,
-                PostDelayedTask(_, transmit_period_milliseconds));
-  }
-  void SendNextRequest() {
-    EXPECT_CALL(monitor_, CreateClient())
-        .WillOnce(Invoke(this, &LinkMonitorTest::CreateMockClient));
-    EXPECT_CALL(*next_client_, TransmitRequest(_)).WillOnce(Return(true));
-    EXPECT_CALL(dispatcher_,
-                PostDelayedTask(_, GetCurrentTestPeriodMilliseconds()));
-    TriggerRequestTimer();
-  }
-  void ExpectNoTransmit() {
-    MockArpClient *client =
-      monitor_.arp_client_.get() ? client_ : next_client_;
-    EXPECT_CALL(*client, TransmitRequest(_)).Times(0);
-  }
-  void ExpectRestart(int transmit_period_milliseconds) {
-    EXPECT_CALL(device_info_, GetMACAddress(0, _))
-        .WillOnce(DoAll(SetArgumentPointee<1>(local_mac_), Return(true)));
-    // Can't just use ExpectTransmit, because that depends on state
-    // that changes during Stop.
-    EXPECT_CALL(monitor_, CreateClient())
-        .WillOnce(Invoke(this, &LinkMonitorTest::CreateMockClient));
-    EXPECT_CALL(*next_client_, TransmitRequest(
-        IsArpRequest(local_ip_, gateway_ip_, local_mac_, zero_mac_)))
-        .WillOnce(Return(true));
-    EXPECT_CALL(dispatcher_,
-                PostDelayedTask(_, transmit_period_milliseconds));
-  }
-  void StartMonitor() {
-    EXPECT_CALL(device_info_, GetMACAddress(0, _))
-        .WillOnce(DoAll(SetArgumentPointee<1>(local_mac_), Return(true)));
-    ExpectTransmit(false, GetDefaultTestPeriodMilliseconds());
-    EXPECT_TRUE(monitor_.Start());
-    EXPECT_TRUE(GetArpClient());
-    EXPECT_FALSE(IsUnicast());
-    EXPECT_FALSE(GetSendRequestCallback().IsCancelled());
-  }
-  void ReportResume() {
-    monitor_.OnAfterResume();
-  }
-  bool SimulateReceivePacket(ArpPacket *packet, ByteString *sender) {
-    packet->set_operation(rx_packet_.operation());
-    packet->set_local_ip_address(rx_packet_.local_ip_address());
-    packet->set_remote_ip_address(rx_packet_.remote_ip_address());
-    packet->set_local_mac_address(rx_packet_.local_mac_address());
-    packet->set_remote_mac_address(rx_packet_.remote_mac_address());
-    return true;
-  }
-  void ReceiveResponse(uint16_t operation,
-                       const IPAddress &local_ip,
-                       const ByteString &local_mac,
-                       const IPAddress &remote_ip,
-                       const ByteString &remote_mac) {
-    rx_packet_.set_operation(operation);
-    rx_packet_.set_local_ip_address(local_ip);
-    rx_packet_.set_local_mac_address(local_mac);
-    rx_packet_.set_remote_ip_address(remote_ip);
-    rx_packet_.set_remote_mac_address(remote_mac);
-
-    EXPECT_CALL(*client_, ReceivePacket(_, _))
-        .WillOnce(Invoke(this, &LinkMonitorTest::SimulateReceivePacket));
-    monitor_.ReceiveResponse(0);
-  }
-  void ReceiveCorrectResponse() {
-    ReceiveResponse(ARPOP_REPLY, gateway_ip_, gateway_mac_,
-                    local_ip_, local_mac_);
-  }
-  void RunUnicastResponseCycle(int cycle_count,
-                               bool should_respond_to_unicast_probes,
-                               bool should_count_failures) {
-    // This method expects the LinkMonitor to be in a state where it
-    // is waiting for a broadcast response.  It also returns with the
-    // LinkMonitor in the same state.
-    // Successful receptions.
-    EXPECT_CALL(metrics_, SendToUMA(
-        HasSubstr("LinkMonitorResponseTimeSample"), 0, _, _, _))
-        .Times(cycle_count * (should_respond_to_unicast_probes ? 2 : 1));
-    // Unsuccessful unicast receptions.
-    EXPECT_CALL(metrics_, SendToUMA(
-        HasSubstr("LinkMonitorResponseTimeSample"),
-        GetDefaultTestPeriodMilliseconds(),
-        _, _, _)).Times(cycle_count *
-                        (should_respond_to_unicast_probes ? 0 : 1));
-
-    // Account for any successes / failures before we started.
-    int expected_broadcast_success_count = GetBroadcastSuccessCount();
-    int expected_unicast_success_count = GetUnicastSuccessCount();
-    int expected_unicast_failure_count = GetUnicastFailureCount();
-
-    for (int i = 0; i < cycle_count; ++i) {
-      // Respond to the pending broadcast request.
-      ReceiveCorrectResponse();
-
-      // Unicast ARP.
-      ExpectTransmit(true, GetDefaultTestPeriodMilliseconds());
-      TriggerRequestTimer();
-      if (should_respond_to_unicast_probes) ReceiveCorrectResponse();
-
-      // Initiate broadcast ARP.
-      ExpectTransmit(false, GetDefaultTestPeriodMilliseconds());
-      TriggerRequestTimer();
-
-      ++expected_broadcast_success_count;
-      if (should_respond_to_unicast_probes) {
-        ++expected_unicast_success_count;
-        expected_unicast_failure_count = 0;
-      } else {
-        if (should_count_failures) {
-          ++expected_unicast_failure_count;
-        }
-        expected_unicast_success_count = 0;
-      }
-      EXPECT_EQ(expected_unicast_failure_count, GetUnicastFailureCount());
-      EXPECT_EQ(expected_unicast_success_count, GetUnicastSuccessCount());
-      EXPECT_EQ(0, GetBroadcastFailureCount());
-      EXPECT_EQ(expected_broadcast_success_count, GetBroadcastSuccessCount());
-    }
-  }
-  bool IsGatewayFound() {
-    return monitor_.IsGatewayFound();
-  }
-
   MockEventDispatcher dispatcher_;
   StrictMock<MockMetrics> metrics_;
   MockControl control_;
   NiceMock<MockDeviceInfo> device_info_;
   scoped_refptr<MockConnection> connection_;
-  StrictMock<LinkMonitorForTest> monitor_;
   MockTime time_;
   struct timeval time_val_;
-  // This is owned by the LinkMonitor, and only tracked here for EXPECT*().
-  MockArpClient *client_;
-  // This is owned by the test until it is handed to the LinkMonitorForTest
-  // and placed // in client_ above.
-  MockArpClient *next_client_;
-  IPAddress gateway_ip_;
-  IPAddress local_ip_;
-  ByteString gateway_mac_;
-  ByteString local_mac_;
-  ByteString zero_mac_;
-  ArpPacket rx_packet_;
-  bool link_scope_logging_was_enabled_;
-  const string interface_name_;
+  MockActiveLinkMonitor *active_link_monitor_;
+  MockPassiveLinkMonitor *passive_link_monitor_;
+  LinkMonitorObserver observer_;
+  LinkMonitor monitor_;
 };
 
-
-TEST_F(LinkMonitorTest, Constructor) {
-  ExpectReset();
+MATCHER_P(IsMacAddress, mac_address, "") {
+  return mac_address.Equals(arg);
 }
 
-TEST_F(LinkMonitorTest, StartFailedGetMACAddress) {
-  ScopedMockLog log;
-  EXPECT_CALL(log, Log(_, _, _)).Times(AnyNumber());
-  EXPECT_CALL(log,
-      Log(logging::LOG_ERROR, _,
-          HasSubstr("Could not get local MAC address"))).Times(1);
-  EXPECT_CALL(device_info_, GetMACAddress(0, _)).WillOnce(Return(false));
-  EXPECT_CALL(metrics_, SendEnumToUMA(
-      HasSubstr("LinkMonitorFailure"), Metrics::kLinkMonitorMacAddressNotFound,
-      _));
-  EXPECT_CALL(monitor_, CreateClient()).Times(0);
+TEST_F(LinkMonitorTest, Start) {
+  EXPECT_CALL(*active_link_monitor_,
+              Start(ActiveLinkMonitor::kDefaultTestPeriodMilliseconds))
+      .WillOnce(Return(false));
   EXPECT_FALSE(monitor_.Start());
-  ExpectReset();
-}
+  Mock::VerifyAndClearExpectations(active_link_monitor_);
 
-TEST_F(LinkMonitorTest, StartFailedCreateClient) {
-  ScopedMockLog log;
-  EXPECT_CALL(log, Log(_, _, _)).Times(AnyNumber());
-  EXPECT_CALL(log,
-      Log(logging::LOG_ERROR, _,
-          HasSubstr("Failed to start ARP client"))).Times(1);
-  EXPECT_CALL(metrics_, SendEnumToUMA(
-      HasSubstr("LinkMonitorFailure"), Metrics::kLinkMonitorClientStartFailure,
-      _));
-  EXPECT_CALL(device_info_, GetMACAddress(0, _)).WillOnce(Return(true));
-  EXPECT_CALL(monitor_, CreateClient()).WillOnce(Return(false));
-  EXPECT_FALSE(monitor_.Start());
-  ExpectReset();
-}
-
-TEST_F(LinkMonitorTest, StartFailedTransmitRequest) {
-  ScopedMockLog log;
-  EXPECT_CALL(log, Log(_, _, _)).Times(AnyNumber());
-  EXPECT_CALL(log,
-      Log(logging::LOG_ERROR, _,
-          HasSubstr("Failed to send ARP"))).Times(1);
-  EXPECT_CALL(metrics_, SendEnumToUMA(
-      HasSubstr("LinkMonitorFailure"), Metrics::kLinkMonitorTransmitFailure,
-      _));
-  EXPECT_CALL(device_info_, GetMACAddress(0, _)).WillOnce(Return(true));
-  EXPECT_CALL(monitor_, CreateClient())
-      .WillOnce(Invoke(this, &LinkMonitorTest::CreateMockClient));
-  EXPECT_CALL(*next_client_, TransmitRequest(_)).WillOnce(Return(false));
-  EXPECT_FALSE(monitor_.Start());
-  ExpectReset();
-}
-
-TEST_F(LinkMonitorTest, StartSuccess) {
-  StartMonitor();
-}
-
-TEST_F(LinkMonitorTest, Stop) {
-  StartMonitor();
-  monitor_.Stop();
-  ExpectReset();
-}
-
-TEST_F(LinkMonitorTest, ReplyReception) {
-  StartMonitor();
-  const int kResponseTime = 1234;
-  AdvanceTime(kResponseTime);
-  ScopedMockLog log;
-
-  EXPECT_CALL(log, Log(_, _, _)).Times(AnyNumber());
-  EXPECT_CALL(log, Log(_, _, HasSubstr("not for our IP"))).Times(1);
-  ReceiveResponse(ARPOP_REPLY, gateway_ip_, gateway_mac_,
-                  gateway_ip_, local_mac_);
-  Mock::VerifyAndClearExpectations(&log);
-
-  EXPECT_CALL(log, Log(_, _, _)).Times(AnyNumber());
-  EXPECT_CALL(log, Log(_, _, HasSubstr("not for our MAC"))).Times(1);
-  ReceiveResponse(ARPOP_REPLY, gateway_ip_, gateway_mac_,
-                  local_ip_, gateway_mac_);
-  Mock::VerifyAndClearExpectations(&log);
-
-  EXPECT_CALL(log, Log(_, _, _)).Times(AnyNumber());
-  EXPECT_CALL(log, Log(_, _, HasSubstr("not from the gateway"))).Times(1);
-  ReceiveResponse(ARPOP_REPLY, local_ip_, gateway_mac_, local_ip_, local_mac_);
-  Mock::VerifyAndClearExpectations(&log);
-
-  EXPECT_CALL(log, Log(_, _, _)).Times(AnyNumber());
-  EXPECT_CALL(log, Log(_, _, HasSubstr("This is not a reply packet"))).Times(1);
-  ReceiveResponse(ARPOP_REQUEST, gateway_ip_, gateway_mac_,
-                  local_ip_, local_mac_);
-  Mock::VerifyAndClearExpectations(&log);
-
-  EXPECT_TRUE(GetArpClient());
-  EXPECT_FALSE(monitor_.GetResponseTimeMilliseconds());
-  EXPECT_CALL(log, Log(_, _, _)).Times(AnyNumber());
-  EXPECT_CALL(log, Log(_, _, HasSubstr("Found gateway"))).Times(1);
-  EXPECT_CALL(metrics_, SendToUMA(
-      HasSubstr("LinkMonitorResponseTimeSample"), kResponseTime,
-       _, _, _)).Times(1);
-  EXPECT_CALL(monitor_, GatewayChangeCallbackHandler()).Times(1);
-  ReceiveCorrectResponse();
-  EXPECT_FALSE(GetArpClient());
-  EXPECT_EQ(kResponseTime, monitor_.GetResponseTimeMilliseconds());
-  EXPECT_TRUE(IsUnicast());
-}
-
-TEST_F(LinkMonitorTest, TimeoutBroadcast) {
-  EXPECT_CALL(metrics_, SendToUMA(
-      HasSubstr("LinkMonitorResponseTimeSample"),
-      GetDefaultTestPeriodMilliseconds(),
-      _, _, _)).Times(GetFailureThreshold());
-  StartMonitor();
-  // This value doesn't match real life (the timer in this scenario
-  // should advance by LinkMonitor::kDefaultTestPeriodMilliseconds),
-  // but this demonstrates the LinkMonitorSecondsToFailure independent
-  // from the response-time figures.
-  const int kTimeIncrement = 1000;
-  for (size_t i = 1; i < GetFailureThreshold(); ++i) {
-    ExpectTransmit(false, GetDefaultTestPeriodMilliseconds());
-    AdvanceTime(kTimeIncrement);
-    TriggerRequestTimer();
-    EXPECT_FALSE(IsUnicast());
-    EXPECT_EQ(i, GetBroadcastFailureCount());
-    EXPECT_EQ(0, GetUnicastFailureCount());
-    EXPECT_EQ(0, GetBroadcastSuccessCount());
-    EXPECT_EQ(0, GetUnicastSuccessCount());
-    EXPECT_EQ(GetDefaultTestPeriodMilliseconds(),
-              monitor_.GetResponseTimeMilliseconds());
-  }
-  ScopedMockLog log;
-  EXPECT_CALL(log, Log(_, _, _)).Times(AnyNumber());
-  EXPECT_CALL(log,
-      Log(logging::LOG_ERROR, _,
-          HasSubstr("monitor has reached the failure threshold"))).Times(1);
-  EXPECT_CALL(metrics_, SendEnumToUMA(
-      HasSubstr("LinkMonitorFailure"),
-      Metrics::kLinkMonitorFailureThresholdReached, _));
-  EXPECT_CALL(metrics_, SendToUMA(
-      HasSubstr("LinkMonitorSecondsToFailure"),
-      kTimeIncrement * GetFailureThreshold() / 1000, _, _, _));
-  EXPECT_CALL(metrics_, SendToUMA(
-      HasSubstr("BroadcastErrorsAtFailure"), GetFailureThreshold(), _, _, _));
-  EXPECT_CALL(metrics_, SendToUMA(
-      HasSubstr("UnicastErrorsAtFailure"), 0, _, _, _));
-  EXPECT_FALSE(GetSendRequestCallback().IsCancelled());
-  ExpectNoTransmit();
-  EXPECT_CALL(monitor_, FailureCallbackHandler());
-  AdvanceTime(kTimeIncrement);
-  TriggerRequestTimer();
-  ExpectReset();
-}
-
-TEST_F(LinkMonitorTest, TimeoutUnicast) {
-  StartMonitor();
-
-  ScopedMockLog log;
-  EXPECT_CALL(log, Log(_, _, _)).Times(AnyNumber());
-  EXPECT_CALL(log,
-      Log(logging::LOG_ERROR, _,
-          HasSubstr("monitor has reached the failure threshold"))).Times(0);
-  EXPECT_CALL(metrics_, SendEnumToUMA(
-      HasSubstr("LinkMonitorFailure"),
-      Metrics::kLinkMonitorFailureThresholdReached, _)).Times(0);
-  EXPECT_CALL(metrics_, SendToUMA(
-      HasSubstr("LinkMonitorSecondsToFailure"), _, _, _, _)).Times(0);
-  EXPECT_CALL(metrics_, SendToUMA(
-      HasSubstr("BroadcastErrorsAtFailure"), _, _, _, _)).Times(0);
-  EXPECT_CALL(metrics_, SendToUMA(
-      HasSubstr("UnicastErrorsAtFailure"), _, _, _, _)).Times(0);
-  EXPECT_CALL(monitor_, GatewayChangeCallbackHandler()).Times(1);
-
-  // Unicast failures should not cause LinkMonitor errors if we haven't
-  // noted the gateway as reliably replying to unicast ARP messages.  Test
-  // this by doing threshold - 1 successful unicast responses, followed
-  // by a ton of unicast failures.
-  RunUnicastResponseCycle(GetUnicastReplyReliabilityThreshold() - 1,
-                          true, false);
-  EXPECT_EQ(GetUnicastReplyReliabilityThreshold() - 1,
-            GetUnicastSuccessCount());
-  RunUnicastResponseCycle(GetFailureThreshold() +
-                          GetUnicastReplyReliabilityThreshold(), false, false);
-  EXPECT_FALSE(GetSendRequestCallback().IsCancelled());
-  EXPECT_FALSE(GatewaySupportsUnicastArp());
-  EXPECT_EQ(0, GetUnicastSuccessCount());
-  EXPECT_EQ(0, GetUnicastFailureCount());
-
-  // Cross the the unicast reliability threshold.
-  RunUnicastResponseCycle(GetUnicastReplyReliabilityThreshold() - 1,
-                          true, false);
-  EXPECT_CALL(log,
-      Log(_, _, HasSubstr("Unicast failures will now count")));
-  EXPECT_FALSE(GatewaySupportsUnicastArp());
-  RunUnicastResponseCycle(1, true, false);
-  EXPECT_TRUE(GatewaySupportsUnicastArp());
-
-  // Induce one less failures than will cause a link monitor failure, and
-  // confirm that these failures are counted.
-  RunUnicastResponseCycle(GetFailureThreshold() - 1, false, true);
-  EXPECT_EQ(GetFailureThreshold() - 1, GetUnicastFailureCount());
-
-  Mock::VerifyAndClearExpectations(&log);
-  EXPECT_CALL(log, Log(_, _, _)).Times(AnyNumber());
-
-  // Induce a final broadcast success followed by a unicast failure.
-  EXPECT_CALL(metrics_, SendToUMA(
-      HasSubstr("LinkMonitorResponseTimeSample"), 0, _, _, _));
-  ReceiveCorrectResponse();
-  ExpectTransmit(true, GetDefaultTestPeriodMilliseconds());
-  TriggerRequestTimer();
-  EXPECT_FALSE(GetSendRequestCallback().IsCancelled());
-
-  EXPECT_CALL(metrics_, SendToUMA(
-      HasSubstr("LinkMonitorResponseTimeSample"),
-      GetDefaultTestPeriodMilliseconds(),
-      _, _, _));
-  EXPECT_CALL(log,
-      Log(logging::LOG_ERROR, _,
-          HasSubstr("monitor has reached the failure threshold"))).Times(1);
-  EXPECT_CALL(metrics_, SendEnumToUMA(
-      HasSubstr("LinkMonitorFailure"),
-      Metrics::kLinkMonitorFailureThresholdReached, _));
-  EXPECT_CALL(metrics_, SendToUMA(
-      HasSubstr("LinkMonitorSecondsToFailure"), 0, _, _, _));
-  EXPECT_CALL(metrics_, SendToUMA(
-      HasSubstr("BroadcastErrorsAtFailure"), 0, _, _, _));
-  EXPECT_CALL(metrics_, SendToUMA(
-      HasSubstr("UnicastErrorsAtFailure"), GetFailureThreshold(), _, _, _));
-  EXPECT_CALL(monitor_, FailureCallbackHandler());
-  ExpectNoTransmit();
-  TriggerRequestTimer();
-  ExpectReset();
+  EXPECT_CALL(*active_link_monitor_,
+              Start(ActiveLinkMonitor::kDefaultTestPeriodMilliseconds))
+      .WillOnce(Return(true));
+  EXPECT_TRUE(monitor_.Start());
+  Mock::VerifyAndClearExpectations(active_link_monitor_);
 }
 
 TEST_F(LinkMonitorTest, OnAfterResume) {
-  const int kFastTestPeriodMilliseconds = GetFastTestPeriodMilliseconds();
-  StartMonitor();
-  Mock::VerifyAndClearExpectations(&monitor_);
+  ByteString gateway_mac(kGatewayMACAddress, arraysize(kGatewayMACAddress));
+  const bool kGatewayUnicastArpSupport = true;
+  SetGatewayMacAddress(gateway_mac);
+  // Verify gateway settings persist when link monitor is restarted, and
+  // active link monitor is started with fast test period.
+  EXPECT_CALL(*active_link_monitor_, Stop()).Times(1);
+  EXPECT_CALL(*passive_link_monitor_, Stop()).Times(1);
+  EXPECT_CALL(*active_link_monitor_, gateway_supports_unicast_arp())
+      .WillOnce(Return(kGatewayUnicastArpSupport));
+  EXPECT_CALL(*active_link_monitor_,
+              set_gateway_mac_address(IsMacAddress(gateway_mac)));
+  EXPECT_CALL(*active_link_monitor_,
+              set_gateway_supports_unicast_arp(kGatewayUnicastArpSupport));
+  EXPECT_CALL(*active_link_monitor_,
+              Start(ActiveLinkMonitor::kFastTestPeriodMilliseconds));
+  monitor_.OnAfterResume();
+  VerifyGatewayMacAddress(gateway_mac);
+  Mock::VerifyAndClearExpectations(active_link_monitor_);
+  Mock::VerifyAndClearExpectations(passive_link_monitor_);
+}
 
-  // Resume should preserve the fact that we haven't resolved the gateway's MAC,
-  // as well as the fact that the gateway suppports unicast ARP.
-  EXPECT_FALSE(IsGatewayFound());
-  EXPECT_FALSE(GatewaySupportsUnicastArp());
-  ExpectRestart(kFastTestPeriodMilliseconds);
-  ReportResume();
-  EXPECT_FALSE(IsGatewayFound());
-  EXPECT_FALSE(GatewaySupportsUnicastArp());
+TEST_F(LinkMonitorTest, OnActiveLinkMonitorFailure) {
+  // Start link monitor.
+  EXPECT_CALL(*active_link_monitor_,
+              Start(ActiveLinkMonitor::kDefaultTestPeriodMilliseconds))
+      .WillOnce(Return(true));
+  EXPECT_TRUE(monitor_.Start());
+  Mock::VerifyAndClearExpectations(active_link_monitor_);
 
-  // This is the expected normal case, so let's test that.  The
-  // OnAfterResumeWithoutUnicast test below verifies when this is not true.
-  SetGatewaySupportsUnicastArp(true);
+  const int kBroadcastFailureCount = 5;
+  const int kUnicastFailureCount = 3;
+  const int kElapsedTimeMilliseconds = 5000;
 
-  // After resume, we should use the fast test period...
-  ExpectRestart(kFastTestPeriodMilliseconds);
-  ReportResume();
-  EXPECT_EQ(kFastTestPeriodMilliseconds, GetCurrentTestPeriodMilliseconds());
-
-  // ...and the fast period should be used for reporting failure to UMA...
-  EXPECT_CALL(metrics_, SendToUMA(
-      HasSubstr("LinkMonitorResponseTimeSample"), kFastTestPeriodMilliseconds,
-      _, _, _));
-  ExpectTransmit(false, kFastTestPeriodMilliseconds);
-  TriggerRequestTimer();
-
-  // ...and the period should be reset after correct responses on both
-  // broadcast and unicast.
-  const int kResponseTime = 12;
-  EXPECT_CALL(metrics_, SendToUMA(
-      HasSubstr("LinkMonitorResponseTimeSample"), kResponseTime,  _, _, _))
-      .Times(2);
-  EXPECT_CALL(monitor_, GatewayChangeCallbackHandler()).Times(1);
-  AdvanceTime(kResponseTime);
-  ReceiveCorrectResponse();
-  EXPECT_EQ(GetFastTestPeriodMilliseconds(),
-            GetCurrentTestPeriodMilliseconds());  // Don't change yet.
-  ExpectTransmit(true, kFastTestPeriodMilliseconds);
-  TriggerRequestTimer();
-  AdvanceTime(kResponseTime);
-  ReceiveCorrectResponse();
-  EXPECT_EQ(1, GetBroadcastSuccessCount());
-  EXPECT_EQ(1, GetUnicastSuccessCount());
-  EXPECT_EQ(GetDefaultTestPeriodMilliseconds(),
-            GetCurrentTestPeriodMilliseconds());
-
-  // Resume should preserve the fact that we _have_ resolved the gateway's MAC.
-  EXPECT_TRUE(IsGatewayFound());
-  ExpectRestart(kFastTestPeriodMilliseconds);
-  ReportResume();
-  EXPECT_TRUE(IsGatewayFound());
-  EXPECT_TRUE(GatewaySupportsUnicastArp());
-
-  // Failure should happen just like normal.
-  ExpectRestart(kFastTestPeriodMilliseconds);
-  ReportResume();
-  EXPECT_CALL(metrics_, SendToUMA(
-      HasSubstr("LinkMonitorResponseTimeSample"), kFastTestPeriodMilliseconds,
-      _, _, _))
-      .Times(GetFailureThreshold());
+  // Active monitor failed after 5 seconds.
+  EXPECT_CALL(observer_, OnFailureCallback()).Times(1);
   EXPECT_CALL(metrics_, SendEnumToUMA(
       HasSubstr("LinkMonitorFailure"),
       Metrics::kLinkMonitorFailureThresholdReached, _));
   EXPECT_CALL(metrics_, SendToUMA(
-      HasSubstr("LinkMonitorSecondsToFailure"), _, _, _, _));
-  EXPECT_CALL(metrics_, SendToUMA(
-      HasSubstr("BroadcastErrorsAtFailure"), GetFailureThreshold() / 2 + 1,
+      HasSubstr("LinkMonitorSecondsToFailure"), kElapsedTimeMilliseconds / 1000,
       _, _, _));
   EXPECT_CALL(metrics_, SendToUMA(
-      HasSubstr("UnicastErrorsAtFailure"), GetFailureThreshold() / 2,
+      HasSubstr("BroadcastErrorsAtFailure"), kBroadcastFailureCount,
       _, _, _));
-  EXPECT_CALL(monitor_, FailureCallbackHandler());
-  bool unicast_probe = true;
-  for (size_t i = 1 ; i < GetFailureThreshold(); ++i) {
-    ExpectTransmit(unicast_probe, GetFastTestPeriodMilliseconds());
-    TriggerRequestTimer();
-    unicast_probe = !unicast_probe;
-  }
-  TriggerRequestTimer();
-  ExpectReset();
-}
-
-TEST_F(LinkMonitorTest, OnAfterResumeWithoutUnicast) {
-  const int kFastTestPeriodMilliseconds = GetFastTestPeriodMilliseconds();
-  StartMonitor();
-  Mock::VerifyAndClearExpectations(&monitor_);
-
-  // Resume should preserve the fact that we haven't resolved the gateway's MAC.
-  EXPECT_FALSE(IsGatewayFound());
-  EXPECT_FALSE(GatewaySupportsUnicastArp());
-  ExpectRestart(kFastTestPeriodMilliseconds);
-  ReportResume();
-  EXPECT_FALSE(IsGatewayFound());
-  EXPECT_FALSE(GatewaySupportsUnicastArp());
-
-  // After resume, we should use the fast test period...
-  ExpectRestart(kFastTestPeriodMilliseconds);
-  ReportResume();
-  EXPECT_EQ(kFastTestPeriodMilliseconds, GetCurrentTestPeriodMilliseconds());
-
-  // ...and the fast period should be used for reporting failure to UMA...
   EXPECT_CALL(metrics_, SendToUMA(
-      HasSubstr("LinkMonitorResponseTimeSample"), kFastTestPeriodMilliseconds,
+      HasSubstr("UnicastErrorsAtFailure"), kUnicastFailureCount,
       _, _, _));
-  ExpectTransmit(false, kFastTestPeriodMilliseconds);
-  TriggerRequestTimer();
-
-  // ...and the period should be reset after correct response on just the
-  // broadcast response since the link monitor doesn't trust unicast.
-  const int kResponseTime = 12;
-  EXPECT_CALL(metrics_, SendToUMA(
-      HasSubstr("LinkMonitorResponseTimeSample"), kResponseTime,  _, _, _));
-  AdvanceTime(kResponseTime);
-  EXPECT_CALL(monitor_, GatewayChangeCallbackHandler()).Times(1);
-  ReceiveCorrectResponse();
-  EXPECT_EQ(GetDefaultTestPeriodMilliseconds(),
-            GetCurrentTestPeriodMilliseconds());
+  AdvanceTime(kElapsedTimeMilliseconds);
+  TriggerActiveLinkMonitorFailure(Metrics::kLinkMonitorFailureThresholdReached,
+                                  kBroadcastFailureCount,
+                                  kUnicastFailureCount);
 }
 
-TEST_F(LinkMonitorTest, Average) {
-  const int kSamples[] = { 200, 950, 1200, 4096, 5000,
-                           86, 120, 3060, 842, 750 };
-  const size_t filter_depth = GetMaxResponseSampleFilterDepth();
-  EXPECT_CALL(metrics_, SendToUMA(
-      HasSubstr("LinkMonitorResponseTimeSample"), _, _, _, _))
-      .Times(arraysize(kSamples));
-  EXPECT_CALL(monitor_, GatewayChangeCallbackHandler()).Times(1);
-  ASSERT_GT(arraysize(kSamples), filter_depth);
-  StartMonitor();
-  size_t i = 0;
-  int sum = 0;
-  for (; i < filter_depth; ++i) {
-    AdvanceTime(kSamples[i]);
-    ReceiveCorrectResponse();
-    sum += kSamples[i];
-    EXPECT_EQ(sum / (i + 1), monitor_.GetResponseTimeMilliseconds());
-    SendNextRequest();
-  }
-  for (; i < arraysize(kSamples); ++i) {
-    AdvanceTime(kSamples[i]);
-    ReceiveCorrectResponse();
-    sum = (sum + kSamples[i]) * filter_depth / (filter_depth + 1);
-    EXPECT_EQ(sum / filter_depth, monitor_.GetResponseTimeMilliseconds());
-    SendNextRequest();
-  }
+TEST_F(LinkMonitorTest, OnActiveLinkMonitorSuccess) {
+  ByteString gateway_mac(kGatewayMACAddress,
+                               arraysize(kGatewayMACAddress));
+  EXPECT_CALL(*active_link_monitor_, gateway_mac_address())
+      .WillRepeatedly(ReturnRef(gateway_mac));
+
+  // Active link monitor succeed for the first time, gateway MAC address will be
+  // updated.
+  EXPECT_CALL(observer_, OnGatewayChangeCallback()).Times(1);
+  EXPECT_CALL(*passive_link_monitor_, Start(
+      PassiveLinkMonitor::kDefaultMonitorCycles)).Times(1);
+  TriggerActiveLinkMonitorSuccess();
+  VerifyGatewayMacAddress(gateway_mac);
+  Mock::VerifyAndClearExpectations(&observer_);
+  Mock::VerifyAndClearExpectations(passive_link_monitor_);
+
+  // Active link monitor succeed again, gateway MAC address not changed.
+  EXPECT_CALL(observer_, OnGatewayChangeCallback()).Times(0);
+  EXPECT_CALL(*passive_link_monitor_, Start(
+      PassiveLinkMonitor::kDefaultMonitorCycles)).Times(1);
+  TriggerActiveLinkMonitorSuccess();
+  VerifyGatewayMacAddress(gateway_mac);
+  Mock::VerifyAndClearExpectations(&observer_);
+  Mock::VerifyAndClearExpectations(passive_link_monitor_);
 }
 
-TEST_F(LinkMonitorTest, ImpulseResponse) {
-  const int kNormalValue = 50;
-  const int kExceptionalValue = 5000;
-  const int filter_depth = GetMaxResponseSampleFilterDepth();
-  EXPECT_CALL(metrics_, SendToUMA(
-      HasSubstr("LinkMonitorResponseTimeSample"), _, _, _, _))
-      .Times(AnyNumber());
-  EXPECT_CALL(monitor_, GatewayChangeCallbackHandler()).Times(1);
-  StartMonitor();
-  for (int i = 0; i < filter_depth * 2; ++i) {
-    AdvanceTime(kNormalValue);
-    ReceiveCorrectResponse();
-    EXPECT_EQ(kNormalValue, monitor_.GetResponseTimeMilliseconds());
-    SendNextRequest();
-  }
-  AdvanceTime(kExceptionalValue);
-  ReceiveCorrectResponse();
-  // Our expectation is that an impulse input will be a
-  // impulse_height / (filter_depth + 1) increase to the running average.
-  int expected_impulse_response =
-      kNormalValue + (kExceptionalValue - kNormalValue) / (filter_depth + 1);
-  EXPECT_EQ(expected_impulse_response, monitor_.GetResponseTimeMilliseconds());
-  SendNextRequest();
+TEST_F(LinkMonitorTest, OnPassiveLinkMonitorResultCallback) {
+  // Active link monitor should start regardless of the result of the passive
+  // link monitor.
 
-  // From here, if we end up continuing to receive normal values, our
-  // running average should decay backwards to the normal value.
-  const int failsafe = 100;
-  int last_value = monitor_.GetResponseTimeMilliseconds();
-  for (int i = 0; i < failsafe && last_value != kNormalValue; ++i) {
-    AdvanceTime(kNormalValue);
-    ReceiveCorrectResponse();
-    // We should advance monotonically (but not necessarily linearly)
-    // back towards the normal value.
-    EXPECT_GE(last_value, monitor_.GetResponseTimeMilliseconds());
-    SendNextRequest();
-    last_value = monitor_.GetResponseTimeMilliseconds();
-  }
-  EXPECT_EQ(kNormalValue, last_value);
-}
+  EXPECT_CALL(*active_link_monitor_,
+              Start(ActiveLinkMonitor::kDefaultTestPeriodMilliseconds));
+  TriggerPassiveLinkMonitorResultCallback(true);
+  Mock::VerifyAndClearExpectations(active_link_monitor_);
 
-TEST_F(LinkMonitorTest, HardwareAddressToString) {
-  const uint8_t address0[] = { 0, 1, 2, 3, 4, 5 };
-  EXPECT_EQ("00:01:02:03:04:05",
-            HardwareAddressToString(ByteString(address0, arraysize(address0))));
-  const uint8_t address1[] = { 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd };
-  EXPECT_EQ("88:99:aa:bb:cc:dd",
-            HardwareAddressToString(ByteString(address1, arraysize(address1))));
+  EXPECT_CALL(*active_link_monitor_,
+              Start(ActiveLinkMonitor::kDefaultTestPeriodMilliseconds));
+  TriggerPassiveLinkMonitorResultCallback(false);
+  Mock::VerifyAndClearExpectations(active_link_monitor_);
 }
 
 }  // namespace shill
diff --git a/mock_active_link_monitor.h b/mock_active_link_monitor.h
index 96e8b02..47104a5 100644
--- a/mock_active_link_monitor.h
+++ b/mock_active_link_monitor.h
@@ -12,6 +12,8 @@
 
 namespace shill {
 
+class Connection;
+
 class MockActiveLinkMonitor : public ActiveLinkMonitor {
  public:
   MockActiveLinkMonitor();
@@ -19,6 +21,10 @@
 
   MOCK_METHOD1(Start, bool(int));
   MOCK_METHOD0(Stop, void());
+  MOCK_CONST_METHOD0(gateway_mac_address, const ByteString &());
+  MOCK_METHOD1(set_gateway_mac_address, void(const ByteString &));
+  MOCK_CONST_METHOD0(gateway_supports_unicast_arp, bool());
+  MOCK_METHOD1(set_gateway_supports_unicast_arp, void(bool));
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockActiveLinkMonitor);
diff --git a/mock_passive_link_monitor.h b/mock_passive_link_monitor.h
index 4838b4c..d189718 100644
--- a/mock_passive_link_monitor.h
+++ b/mock_passive_link_monitor.h
@@ -12,6 +12,8 @@
 
 namespace shill {
 
+class Connection;
+
 class MockPassiveLinkMonitor : public PassiveLinkMonitor {
  public:
   MockPassiveLinkMonitor();
diff --git a/passive_link_monitor.cc b/passive_link_monitor.cc
index fce1917..5757960 100644
--- a/passive_link_monitor.cc
+++ b/passive_link_monitor.cc
@@ -36,13 +36,14 @@
                                        const ResultCallback &result_callback)
     : connection_(connection),
       dispatcher_(dispatcher),
-      arp_client_(new ArpClient(connection->interface_index())),
+      // 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),
-      monitor_cycle_timeout_callback_(
-          Bind(&PassiveLinkMonitor::CycleTimeoutHandler, Unretained(this))) {
+      num_cycles_passed_(0) {
 }
 
 PassiveLinkMonitor::~PassiveLinkMonitor() {
@@ -50,12 +51,15 @@
 }
 
 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;