blob: 2485ce507b426b9995acc5fac297b0b88c04ac28 [file] [log] [blame]
Paul Stewart3f43f432012-07-16 12:12:45 -07001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "shill/link_monitor.h"
6
Paul Stewart6c72c972012-07-27 11:29:20 -07007#include <vector>
Paul Stewart3f43f432012-07-16 12:12:45 -07008
Paul Stewart6c72c972012-07-27 11:29:20 -07009#include <base/bind.h>
10#include <base/logging.h>
11#include <base/stringprintf.h>
12#include <base/string_util.h>
13
14#include "shill/arp_client.h"
15#include "shill/arp_packet.h"
16#include "shill/byte_string.h"
Paul Stewart3f43f432012-07-16 12:12:45 -070017#include "shill/connection.h"
Paul Stewart6c72c972012-07-27 11:29:20 -070018#include "shill/device_info.h"
Paul Stewart3f43f432012-07-16 12:12:45 -070019#include "shill/event_dispatcher.h"
Paul Stewart6c72c972012-07-27 11:29:20 -070020#include "shill/ip_address.h"
21#include "shill/scope_logger.h"
22#include "shill/shill_time.h"
23
24using base::Bind;
25using base::Unretained;
26using std::string;
Paul Stewart3f43f432012-07-16 12:12:45 -070027
28namespace shill {
29
Paul Stewart6c72c972012-07-27 11:29:20 -070030const unsigned int LinkMonitor::kTestPeriodMilliseconds = 5000;
31const unsigned int LinkMonitor::kFailureThreshold = 5;
32const unsigned int LinkMonitor::kMaxResponseSampleFilterDepth = 5;
33
Paul Stewart3f43f432012-07-16 12:12:45 -070034LinkMonitor::LinkMonitor(const ConnectionRefPtr &connection,
35 EventDispatcher *dispatcher,
Paul Stewart6c72c972012-07-27 11:29:20 -070036 DeviceInfo *device_info,
Paul Stewart3f43f432012-07-16 12:12:45 -070037 const FailureCallback &failure_callback)
38 : connection_(connection),
39 dispatcher_(dispatcher),
Paul Stewart6c72c972012-07-27 11:29:20 -070040 device_info_(device_info),
Paul Stewart3f43f432012-07-16 12:12:45 -070041 failure_callback_(failure_callback),
Paul Stewart6c72c972012-07-27 11:29:20 -070042 broadcast_failure_count_(0),
43 unicast_failure_count_(0),
44 is_unicast_(false),
45 response_sample_count_(0),
46 response_sample_bucket_(0),
47 time_(Time::GetInstance()) {}
Paul Stewart3f43f432012-07-16 12:12:45 -070048
Paul Stewart6c72c972012-07-27 11:29:20 -070049LinkMonitor::~LinkMonitor() {
50 Stop();
51}
Paul Stewart3f43f432012-07-16 12:12:45 -070052
53bool LinkMonitor::Start() {
Paul Stewart6c72c972012-07-27 11:29:20 -070054 Stop();
55
56 if (!device_info_->GetMACAddress(
57 connection_->interface_index(), &local_mac_address_)) {
58 LOG(ERROR) << "Could not get local MAC address.";
59 Stop();
60 return false;
61 }
62 gateway_mac_address_ = ByteString(local_mac_address_.GetLength());
63 send_request_callback_.Reset(
64 Bind(&LinkMonitor::SendRequestTask, Unretained(this)));
65 return SendRequest();
Paul Stewart3f43f432012-07-16 12:12:45 -070066}
67
68void LinkMonitor::Stop() {
Paul Stewart6c72c972012-07-27 11:29:20 -070069 SLOG(Link, 2) << "In " << __func__ << ".";
70 local_mac_address_.Clear();
71 gateway_mac_address_.Clear();
72 arp_client_.reset();
73 broadcast_failure_count_ = 0;
74 unicast_failure_count_ = 0;
75 is_unicast_ = false;
76 response_sample_bucket_ = 0;
77 response_sample_count_ = 0;
78 receive_response_handler_.reset();
79 send_request_callback_.Cancel();
80 timerclear(&sent_request_at_);
81}
82
83unsigned int LinkMonitor::GetResponseTimeMilliseconds() {
84 return response_sample_count_ ?
85 response_sample_bucket_ / response_sample_count_ : 0;
86}
87
88void LinkMonitor::AddResponseTimeSample(
89 unsigned int response_time_milliseconds) {
90 SLOG(Link, 2) << "In " << __func__ << " with sample "
91 << response_time_milliseconds << ".";
92 response_sample_bucket_ += response_time_milliseconds;
93 if (response_sample_count_ < kMaxResponseSampleFilterDepth) {
94 ++response_sample_count_;
95 } else {
96 response_sample_bucket_ =
97 response_sample_bucket_ * kMaxResponseSampleFilterDepth /
98 (kMaxResponseSampleFilterDepth + 1);
99 }
100}
101
102// static
103string LinkMonitor::HardwareAddressToString(const ByteString &address) {
104 std::vector<string> address_parts;
105 for (size_t i = 0; i < address.GetLength(); ++i) {
106 address_parts.push_back(
107 base::StringPrintf("%02x", address.GetConstData()[i]));
108 }
109 return JoinString(address_parts, ':');
110}
111
112bool LinkMonitor::CreateClient() {
113 arp_client_.reset(new ArpClient(connection_->interface_index()));
114
115 if (!arp_client_->Start()) {
116 return false;
117 }
118 receive_response_handler_.reset(
119 dispatcher_->CreateReadyHandler(
120 arp_client_->socket(),
121 IOHandler::kModeInput,
122 Bind(&LinkMonitor::ReceiveResponse, Unretained(this))));
123 return true;
124}
125
126bool LinkMonitor::AddMissedResponse() {
127 SLOG(Link, 2) << "In " << __func__ << ".";
128 AddResponseTimeSample(kTestPeriodMilliseconds);
129
130 if (is_unicast_) {
131 ++unicast_failure_count_;
132 } else {
133 ++broadcast_failure_count_;
134 }
135
136 if (unicast_failure_count_ + broadcast_failure_count_ >= kFailureThreshold) {
137 LOG(ERROR) << "Link monitor has reached the failure threshold with "
138 << broadcast_failure_count_
139 << " broadcast failures and "
140 << unicast_failure_count_
141 << " unicast failures.";
142 failure_callback_.Run();
143 Stop();
144 return true;
145 }
146 is_unicast_ = !is_unicast_;
147 return false;
148}
149
150void LinkMonitor::ReceiveResponse(int fd) {
151 SLOG(Link, 2) << "In " << __func__ << ".";
152 ArpPacket packet;
153 ByteString sender;
154 if (!arp_client_->ReceiveReply(&packet, &sender)) {
155 return;
156 }
157
158 if (!connection_->local().Equals(packet.local_ip_address())) {
159 SLOG(Link, 4) << "Response is not for our IP address.";
160 return;
161 }
162
163 if (!local_mac_address_.Equals(packet.local_mac_address())) {
164 SLOG(Link, 4) << "Response is not for our MAC address.";
165 return;
166 }
167
168 if (!connection_->gateway().Equals(packet.remote_ip_address())) {
169 SLOG(Link, 4) << "Response is not from the gateway IP address.";
170 return;
171 }
172
173 struct timeval now, elapsed_time;
174 time_->GetTimeMonotonic(&now);
175 timersub(&now, &sent_request_at_, &elapsed_time);
176
177 AddResponseTimeSample(elapsed_time.tv_sec * 1000 +
178 elapsed_time.tv_usec / 1000);
179
180 receive_response_handler_.reset();
181 arp_client_.reset();
182
183 if (is_unicast_) {
184 unicast_failure_count_ = 0;
185 } else {
186 broadcast_failure_count_ = 0;
187 }
188
189 if (!gateway_mac_address_.Equals(packet.remote_mac_address())) {
190 const ByteString &new_mac_address = packet.remote_mac_address();
191 if (gateway_mac_address_.IsZero()) {
192 SLOG(Link, 2) << "Found gateway at "
193 << HardwareAddressToString(new_mac_address);
194 } else {
195 SLOG(Link, 2) << "Gateway MAC address changed.";
196 }
197 gateway_mac_address_ = new_mac_address;
198 }
199
200 is_unicast_ = !is_unicast_;
201}
202
203bool LinkMonitor::SendRequest() {
204 SLOG(Link, 2) << "In " << __func__ << ".";
205 if (!arp_client_.get()) {
206 if (!CreateClient()) {
207 LOG(ERROR) << "Failed to start ARP client.";
208 Stop();
209 return false;
210 }
211 } else if (AddMissedResponse()) {
212 // If an ARP client is still listening, this means we have timed
213 // out reception of the ARP reply.
214 return false;
215 } else {
216 // We already have an ArpClient instance running. These aren't
217 // bound sockets in the conventional sense, and we cannot distinguish
218 // which request (from which trial, or even from which component
219 // in the local system) an ARP reply was sent in response to.
220 // Therefore we keep the already open ArpClient in the case of
221 // a non-fatal timeout.
222 }
223
224 ByteString destination_mac_address(gateway_mac_address_.GetLength());
225 if (gateway_mac_address_.IsZero()) {
226 // The remote MAC addess is set by convention to be all-zeroes in the
227 // ARP header if not known. The ArpClient will translate an all-zeroes
228 // remote address into a send to the broadcast (all-ones) address in
229 // the Ethernet frame header.
230 SLOG_IF(Link, 2, is_unicast_) << "Sending broadcast since "
231 << "gateway MAC is unknown";
232 is_unicast_ = false;
233 } else if (is_unicast_) {
234 destination_mac_address = gateway_mac_address_;
235 }
236
237 ArpPacket request(connection_->local(), connection_->gateway(),
238 local_mac_address_, destination_mac_address);
239 if (!arp_client_->TransmitRequest(request)) {
240 LOG(ERROR) << "Failed to send ARP request. Stopping.";
241 Stop();
242 return false;
243 }
244
245 time_->GetTimeMonotonic(&sent_request_at_);
246
247 dispatcher_->PostDelayedTask(send_request_callback_.callback(),
248 kTestPeriodMilliseconds);
249 return true;
250}
251
252void LinkMonitor::SendRequestTask() {
253 SendRequest();
Paul Stewart3f43f432012-07-16 12:12:45 -0700254}
255
256} // namespace shill