blob: 28ebd5b4629f1df09a819e43358518d0042f5e93 [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>
Paul Stewart6c72c972012-07-27 11:29:20 -070010#include <base/stringprintf.h>
11#include <base/string_util.h>
12
13#include "shill/arp_client.h"
14#include "shill/arp_packet.h"
15#include "shill/byte_string.h"
Paul Stewart3f43f432012-07-16 12:12:45 -070016#include "shill/connection.h"
Paul Stewart6c72c972012-07-27 11:29:20 -070017#include "shill/device_info.h"
Paul Stewart3f43f432012-07-16 12:12:45 -070018#include "shill/event_dispatcher.h"
Paul Stewart6c72c972012-07-27 11:29:20 -070019#include "shill/ip_address.h"
Christopher Wileyb691efd2012-08-09 13:51:51 -070020#include "shill/logging.h"
Paul Stewartff845fc2012-08-07 07:28:44 -070021#include "shill/metrics.h"
Paul Stewart6c72c972012-07-27 11:29:20 -070022#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 Stewartff845fc2012-08-07 07:28:44 -070036 Metrics *metrics,
Paul Stewart6c72c972012-07-27 11:29:20 -070037 DeviceInfo *device_info,
Paul Stewart3f43f432012-07-16 12:12:45 -070038 const FailureCallback &failure_callback)
39 : connection_(connection),
40 dispatcher_(dispatcher),
Paul Stewartff845fc2012-08-07 07:28:44 -070041 metrics_(metrics),
Paul Stewart6c72c972012-07-27 11:29:20 -070042 device_info_(device_info),
Paul Stewart3f43f432012-07-16 12:12:45 -070043 failure_callback_(failure_callback),
Paul Stewart6c72c972012-07-27 11:29:20 -070044 broadcast_failure_count_(0),
45 unicast_failure_count_(0),
46 is_unicast_(false),
47 response_sample_count_(0),
48 response_sample_bucket_(0),
49 time_(Time::GetInstance()) {}
Paul Stewart3f43f432012-07-16 12:12:45 -070050
Paul Stewart6c72c972012-07-27 11:29:20 -070051LinkMonitor::~LinkMonitor() {
52 Stop();
53}
Paul Stewart3f43f432012-07-16 12:12:45 -070054
55bool LinkMonitor::Start() {
Paul Stewart6c72c972012-07-27 11:29:20 -070056 Stop();
57
58 if (!device_info_->GetMACAddress(
59 connection_->interface_index(), &local_mac_address_)) {
60 LOG(ERROR) << "Could not get local MAC address.";
Paul Stewartff845fc2012-08-07 07:28:44 -070061 metrics_->NotifyLinkMonitorFailure(
62 connection_->technology(), Metrics::kLinkMonitorMacAddressNotFound);
Paul Stewart6c72c972012-07-27 11:29:20 -070063 Stop();
64 return false;
65 }
66 gateway_mac_address_ = ByteString(local_mac_address_.GetLength());
67 send_request_callback_.Reset(
68 Bind(&LinkMonitor::SendRequestTask, Unretained(this)));
69 return SendRequest();
Paul Stewart3f43f432012-07-16 12:12:45 -070070}
71
72void LinkMonitor::Stop() {
Paul Stewart6c72c972012-07-27 11:29:20 -070073 SLOG(Link, 2) << "In " << __func__ << ".";
74 local_mac_address_.Clear();
75 gateway_mac_address_.Clear();
76 arp_client_.reset();
77 broadcast_failure_count_ = 0;
78 unicast_failure_count_ = 0;
79 is_unicast_ = false;
80 response_sample_bucket_ = 0;
81 response_sample_count_ = 0;
82 receive_response_handler_.reset();
83 send_request_callback_.Cancel();
84 timerclear(&sent_request_at_);
85}
86
87unsigned int LinkMonitor::GetResponseTimeMilliseconds() {
88 return response_sample_count_ ?
89 response_sample_bucket_ / response_sample_count_ : 0;
90}
91
92void LinkMonitor::AddResponseTimeSample(
93 unsigned int response_time_milliseconds) {
94 SLOG(Link, 2) << "In " << __func__ << " with sample "
95 << response_time_milliseconds << ".";
Paul Stewartff845fc2012-08-07 07:28:44 -070096 metrics_->NotifyLinkMonitorResponseTimeSampleAdded(
97 connection_->technology(), response_time_milliseconds);
Paul Stewart6c72c972012-07-27 11:29:20 -070098 response_sample_bucket_ += response_time_milliseconds;
99 if (response_sample_count_ < kMaxResponseSampleFilterDepth) {
100 ++response_sample_count_;
101 } else {
102 response_sample_bucket_ =
103 response_sample_bucket_ * kMaxResponseSampleFilterDepth /
104 (kMaxResponseSampleFilterDepth + 1);
105 }
106}
107
108// static
109string LinkMonitor::HardwareAddressToString(const ByteString &address) {
110 std::vector<string> address_parts;
111 for (size_t i = 0; i < address.GetLength(); ++i) {
112 address_parts.push_back(
113 base::StringPrintf("%02x", address.GetConstData()[i]));
114 }
115 return JoinString(address_parts, ':');
116}
117
118bool LinkMonitor::CreateClient() {
119 arp_client_.reset(new ArpClient(connection_->interface_index()));
120
121 if (!arp_client_->Start()) {
Paul Stewartff845fc2012-08-07 07:28:44 -0700122 return false;
Paul Stewart6c72c972012-07-27 11:29:20 -0700123 }
124 receive_response_handler_.reset(
125 dispatcher_->CreateReadyHandler(
126 arp_client_->socket(),
127 IOHandler::kModeInput,
128 Bind(&LinkMonitor::ReceiveResponse, Unretained(this))));
129 return true;
130}
131
132bool LinkMonitor::AddMissedResponse() {
133 SLOG(Link, 2) << "In " << __func__ << ".";
134 AddResponseTimeSample(kTestPeriodMilliseconds);
135
136 if (is_unicast_) {
137 ++unicast_failure_count_;
138 } else {
139 ++broadcast_failure_count_;
140 }
141
142 if (unicast_failure_count_ + broadcast_failure_count_ >= kFailureThreshold) {
143 LOG(ERROR) << "Link monitor has reached the failure threshold with "
144 << broadcast_failure_count_
145 << " broadcast failures and "
146 << unicast_failure_count_
147 << " unicast failures.";
148 failure_callback_.Run();
149 Stop();
Paul Stewartff845fc2012-08-07 07:28:44 -0700150 metrics_->NotifyLinkMonitorFailure(
151 connection_->technology(),
152 Metrics::kLinkMonitorFailureThresholdReached);
Paul Stewart6c72c972012-07-27 11:29:20 -0700153 return true;
154 }
155 is_unicast_ = !is_unicast_;
156 return false;
157}
158
159void LinkMonitor::ReceiveResponse(int fd) {
160 SLOG(Link, 2) << "In " << __func__ << ".";
161 ArpPacket packet;
162 ByteString sender;
163 if (!arp_client_->ReceiveReply(&packet, &sender)) {
164 return;
165 }
166
167 if (!connection_->local().Equals(packet.local_ip_address())) {
168 SLOG(Link, 4) << "Response is not for our IP address.";
169 return;
170 }
171
172 if (!local_mac_address_.Equals(packet.local_mac_address())) {
173 SLOG(Link, 4) << "Response is not for our MAC address.";
174 return;
175 }
176
177 if (!connection_->gateway().Equals(packet.remote_ip_address())) {
178 SLOG(Link, 4) << "Response is not from the gateway IP address.";
179 return;
180 }
181
182 struct timeval now, elapsed_time;
183 time_->GetTimeMonotonic(&now);
184 timersub(&now, &sent_request_at_, &elapsed_time);
185
186 AddResponseTimeSample(elapsed_time.tv_sec * 1000 +
187 elapsed_time.tv_usec / 1000);
188
189 receive_response_handler_.reset();
190 arp_client_.reset();
191
192 if (is_unicast_) {
193 unicast_failure_count_ = 0;
194 } else {
195 broadcast_failure_count_ = 0;
196 }
197
198 if (!gateway_mac_address_.Equals(packet.remote_mac_address())) {
199 const ByteString &new_mac_address = packet.remote_mac_address();
200 if (gateway_mac_address_.IsZero()) {
201 SLOG(Link, 2) << "Found gateway at "
202 << HardwareAddressToString(new_mac_address);
203 } else {
204 SLOG(Link, 2) << "Gateway MAC address changed.";
205 }
206 gateway_mac_address_ = new_mac_address;
207 }
208
209 is_unicast_ = !is_unicast_;
210}
211
212bool LinkMonitor::SendRequest() {
213 SLOG(Link, 2) << "In " << __func__ << ".";
214 if (!arp_client_.get()) {
215 if (!CreateClient()) {
216 LOG(ERROR) << "Failed to start ARP client.";
217 Stop();
Paul Stewartff845fc2012-08-07 07:28:44 -0700218 metrics_->NotifyLinkMonitorFailure(
219 connection_->technology(), Metrics::kLinkMonitorClientStartFailure);
Paul Stewart6c72c972012-07-27 11:29:20 -0700220 return false;
221 }
222 } else if (AddMissedResponse()) {
223 // If an ARP client is still listening, this means we have timed
224 // out reception of the ARP reply.
225 return false;
226 } else {
227 // We already have an ArpClient instance running. These aren't
228 // bound sockets in the conventional sense, and we cannot distinguish
229 // which request (from which trial, or even from which component
230 // in the local system) an ARP reply was sent in response to.
231 // Therefore we keep the already open ArpClient in the case of
232 // a non-fatal timeout.
233 }
234
235 ByteString destination_mac_address(gateway_mac_address_.GetLength());
236 if (gateway_mac_address_.IsZero()) {
237 // The remote MAC addess is set by convention to be all-zeroes in the
238 // ARP header if not known. The ArpClient will translate an all-zeroes
239 // remote address into a send to the broadcast (all-ones) address in
240 // the Ethernet frame header.
241 SLOG_IF(Link, 2, is_unicast_) << "Sending broadcast since "
242 << "gateway MAC is unknown";
243 is_unicast_ = false;
244 } else if (is_unicast_) {
245 destination_mac_address = gateway_mac_address_;
246 }
247
248 ArpPacket request(connection_->local(), connection_->gateway(),
249 local_mac_address_, destination_mac_address);
250 if (!arp_client_->TransmitRequest(request)) {
251 LOG(ERROR) << "Failed to send ARP request. Stopping.";
252 Stop();
Paul Stewartff845fc2012-08-07 07:28:44 -0700253 metrics_->NotifyLinkMonitorFailure(
254 connection_->technology(), Metrics::kLinkMonitorTransmitFailure);
Paul Stewart6c72c972012-07-27 11:29:20 -0700255 return false;
256 }
257
258 time_->GetTimeMonotonic(&sent_request_at_);
259
260 dispatcher_->PostDelayedTask(send_request_callback_.callback(),
261 kTestPeriodMilliseconds);
262 return true;
263}
264
265void LinkMonitor::SendRequestTask() {
266 SendRequest();
Paul Stewart3f43f432012-07-16 12:12:45 -0700267}
268
269} // namespace shill