blob: 8bc7912d60f3f2830ff5080d56991f33d166fca1 [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 Stewartf1961f82012-09-11 20:45:39 -07007#include <string>
Paul Stewart6c72c972012-07-27 11:29:20 -07008#include <vector>
Paul Stewart3f43f432012-07-16 12:12:45 -07009
Paul Stewart6c72c972012-07-27 11:29:20 -070010#include <base/bind.h>
Paul Stewart6c72c972012-07-27 11:29:20 -070011#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"
Christopher Wileyb691efd2012-08-09 13:51:51 -070021#include "shill/logging.h"
Paul Stewartff845fc2012-08-07 07:28:44 -070022#include "shill/metrics.h"
Paul Stewart6c72c972012-07-27 11:29:20 -070023#include "shill/shill_time.h"
24
25using base::Bind;
26using base::Unretained;
27using std::string;
Paul Stewart3f43f432012-07-16 12:12:45 -070028
29namespace shill {
30
Paul Stewartf1961f82012-09-11 20:45:39 -070031const int LinkMonitor::kTestPeriodMilliseconds = 5000;
Paul Stewart036dba02012-08-07 12:34:41 -070032const char LinkMonitor::kDefaultLinkMonitorTechnologies[] = "wifi";
Paul Stewartf1961f82012-09-11 20:45:39 -070033const int LinkMonitor::kFailureThreshold = 5;
34const int LinkMonitor::kMaxResponseSampleFilterDepth = 5;
Paul Stewart6c72c972012-07-27 11:29:20 -070035
Paul Stewart3f43f432012-07-16 12:12:45 -070036LinkMonitor::LinkMonitor(const ConnectionRefPtr &connection,
37 EventDispatcher *dispatcher,
Paul Stewartff845fc2012-08-07 07:28:44 -070038 Metrics *metrics,
Paul Stewart6c72c972012-07-27 11:29:20 -070039 DeviceInfo *device_info,
Paul Stewart3f43f432012-07-16 12:12:45 -070040 const FailureCallback &failure_callback)
41 : connection_(connection),
42 dispatcher_(dispatcher),
Paul Stewartff845fc2012-08-07 07:28:44 -070043 metrics_(metrics),
Paul Stewart6c72c972012-07-27 11:29:20 -070044 device_info_(device_info),
Paul Stewart3f43f432012-07-16 12:12:45 -070045 failure_callback_(failure_callback),
Paul Stewart6c72c972012-07-27 11:29:20 -070046 broadcast_failure_count_(0),
47 unicast_failure_count_(0),
48 is_unicast_(false),
49 response_sample_count_(0),
50 response_sample_bucket_(0),
Paul Stewartf1961f82012-09-11 20:45:39 -070051 time_(Time::GetInstance()) {
52}
Paul Stewart3f43f432012-07-16 12:12:45 -070053
Paul Stewart6c72c972012-07-27 11:29:20 -070054LinkMonitor::~LinkMonitor() {
55 Stop();
56}
Paul Stewart3f43f432012-07-16 12:12:45 -070057
58bool LinkMonitor::Start() {
Paul Stewart6c72c972012-07-27 11:29:20 -070059 Stop();
60
61 if (!device_info_->GetMACAddress(
Paul Stewartf1961f82012-09-11 20:45:39 -070062 connection_->interface_index(), &local_mac_address_)) {
Paul Stewart6c72c972012-07-27 11:29:20 -070063 LOG(ERROR) << "Could not get local MAC address.";
Paul Stewartff845fc2012-08-07 07:28:44 -070064 metrics_->NotifyLinkMonitorFailure(
Paul Stewart0443aa52012-08-09 10:43:50 -070065 connection_->technology(),
66 Metrics::kLinkMonitorMacAddressNotFound,
67 0, 0, 0);
Paul Stewart6c72c972012-07-27 11:29:20 -070068 Stop();
69 return false;
70 }
71 gateway_mac_address_ = ByteString(local_mac_address_.GetLength());
72 send_request_callback_.Reset(
Paul Stewartf1961f82012-09-11 20:45:39 -070073 Bind(base::IgnoreResult(&LinkMonitor::SendRequest), Unretained(this)));
Paul Stewart0443aa52012-08-09 10:43:50 -070074 time_->GetTimeMonotonic(&started_monitoring_at_);
Paul Stewart6c72c972012-07-27 11:29:20 -070075 return SendRequest();
Paul Stewart3f43f432012-07-16 12:12:45 -070076}
77
78void LinkMonitor::Stop() {
Paul Stewart6c72c972012-07-27 11:29:20 -070079 SLOG(Link, 2) << "In " << __func__ << ".";
80 local_mac_address_.Clear();
81 gateway_mac_address_.Clear();
82 arp_client_.reset();
83 broadcast_failure_count_ = 0;
84 unicast_failure_count_ = 0;
85 is_unicast_ = false;
86 response_sample_bucket_ = 0;
87 response_sample_count_ = 0;
88 receive_response_handler_.reset();
89 send_request_callback_.Cancel();
Paul Stewart0443aa52012-08-09 10:43:50 -070090 timerclear(&started_monitoring_at_);
Paul Stewart6c72c972012-07-27 11:29:20 -070091 timerclear(&sent_request_at_);
92}
93
Paul Stewartf1961f82012-09-11 20:45:39 -070094int LinkMonitor::GetResponseTimeMilliseconds() const {
Paul Stewart6c72c972012-07-27 11:29:20 -070095 return response_sample_count_ ?
96 response_sample_bucket_ / response_sample_count_ : 0;
97}
98
Paul Stewartf1961f82012-09-11 20:45:39 -070099void LinkMonitor::AddResponseTimeSample(int response_time_milliseconds) {
Paul Stewart6c72c972012-07-27 11:29:20 -0700100 SLOG(Link, 2) << "In " << __func__ << " with sample "
101 << response_time_milliseconds << ".";
Paul Stewartff845fc2012-08-07 07:28:44 -0700102 metrics_->NotifyLinkMonitorResponseTimeSampleAdded(
103 connection_->technology(), response_time_milliseconds);
Paul Stewart6c72c972012-07-27 11:29:20 -0700104 response_sample_bucket_ += response_time_milliseconds;
105 if (response_sample_count_ < kMaxResponseSampleFilterDepth) {
106 ++response_sample_count_;
107 } else {
108 response_sample_bucket_ =
109 response_sample_bucket_ * kMaxResponseSampleFilterDepth /
110 (kMaxResponseSampleFilterDepth + 1);
111 }
112}
113
114// static
115string LinkMonitor::HardwareAddressToString(const ByteString &address) {
116 std::vector<string> address_parts;
117 for (size_t i = 0; i < address.GetLength(); ++i) {
118 address_parts.push_back(
119 base::StringPrintf("%02x", address.GetConstData()[i]));
120 }
121 return JoinString(address_parts, ':');
122}
123
124bool LinkMonitor::CreateClient() {
125 arp_client_.reset(new ArpClient(connection_->interface_index()));
126
127 if (!arp_client_->Start()) {
Paul Stewartff845fc2012-08-07 07:28:44 -0700128 return false;
Paul Stewart6c72c972012-07-27 11:29:20 -0700129 }
Paul Stewart9f7823e2012-08-09 10:58:26 -0700130 SLOG(Link, 4) << "Created ARP client; listening on socket "
131 << arp_client_->socket() << ".";
Paul Stewart6c72c972012-07-27 11:29:20 -0700132 receive_response_handler_.reset(
133 dispatcher_->CreateReadyHandler(
134 arp_client_->socket(),
135 IOHandler::kModeInput,
136 Bind(&LinkMonitor::ReceiveResponse, Unretained(this))));
137 return true;
138}
139
140bool LinkMonitor::AddMissedResponse() {
141 SLOG(Link, 2) << "In " << __func__ << ".";
142 AddResponseTimeSample(kTestPeriodMilliseconds);
143
144 if (is_unicast_) {
145 ++unicast_failure_count_;
146 } else {
147 ++broadcast_failure_count_;
148 }
149
150 if (unicast_failure_count_ + broadcast_failure_count_ >= kFailureThreshold) {
151 LOG(ERROR) << "Link monitor has reached the failure threshold with "
152 << broadcast_failure_count_
153 << " broadcast failures and "
154 << unicast_failure_count_
155 << " unicast failures.";
156 failure_callback_.Run();
Paul Stewart0443aa52012-08-09 10:43:50 -0700157
158 struct timeval now, elapsed_time;
159 time_->GetTimeMonotonic(&now);
160 timersub(&now, &started_monitoring_at_, &elapsed_time);
161
Paul Stewartff845fc2012-08-07 07:28:44 -0700162 metrics_->NotifyLinkMonitorFailure(
163 connection_->technology(),
Paul Stewart0443aa52012-08-09 10:43:50 -0700164 Metrics::kLinkMonitorFailureThresholdReached,
165 elapsed_time.tv_sec,
166 broadcast_failure_count_,
167 unicast_failure_count_);
168
169 Stop();
Paul Stewart6c72c972012-07-27 11:29:20 -0700170 return true;
171 }
172 is_unicast_ = !is_unicast_;
173 return false;
174}
175
Paul Stewart9f7823e2012-08-09 10:58:26 -0700176bool LinkMonitor::IsGatewayFound() const {
177 return !gateway_mac_address_.IsZero();
178}
179
Paul Stewart6c72c972012-07-27 11:29:20 -0700180void LinkMonitor::ReceiveResponse(int fd) {
181 SLOG(Link, 2) << "In " << __func__ << ".";
182 ArpPacket packet;
183 ByteString sender;
184 if (!arp_client_->ReceiveReply(&packet, &sender)) {
185 return;
186 }
187
Paul Stewart9f7823e2012-08-09 10:58:26 -0700188 if (!connection_->local().address().Equals(
189 packet.remote_ip_address().address())) {
Paul Stewart6c72c972012-07-27 11:29:20 -0700190 SLOG(Link, 4) << "Response is not for our IP address.";
191 return;
192 }
193
Paul Stewart9f7823e2012-08-09 10:58:26 -0700194 if (!local_mac_address_.Equals(packet.remote_mac_address())) {
Paul Stewart6c72c972012-07-27 11:29:20 -0700195 SLOG(Link, 4) << "Response is not for our MAC address.";
196 return;
197 }
198
Paul Stewart9f7823e2012-08-09 10:58:26 -0700199 if (!connection_->gateway().address().Equals(
200 packet.local_ip_address().address())) {
Paul Stewart6c72c972012-07-27 11:29:20 -0700201 SLOG(Link, 4) << "Response is not from the gateway IP address.";
202 return;
203 }
204
205 struct timeval now, elapsed_time;
206 time_->GetTimeMonotonic(&now);
207 timersub(&now, &sent_request_at_, &elapsed_time);
208
209 AddResponseTimeSample(elapsed_time.tv_sec * 1000 +
210 elapsed_time.tv_usec / 1000);
211
212 receive_response_handler_.reset();
213 arp_client_.reset();
214
215 if (is_unicast_) {
216 unicast_failure_count_ = 0;
217 } else {
218 broadcast_failure_count_ = 0;
219 }
220
Paul Stewart9f7823e2012-08-09 10:58:26 -0700221 if (!gateway_mac_address_.Equals(packet.local_mac_address())) {
222 const ByteString &new_mac_address = packet.local_mac_address();
223 if (!IsGatewayFound()) {
Paul Stewart6c72c972012-07-27 11:29:20 -0700224 SLOG(Link, 2) << "Found gateway at "
225 << HardwareAddressToString(new_mac_address);
226 } else {
227 SLOG(Link, 2) << "Gateway MAC address changed.";
228 }
229 gateway_mac_address_ = new_mac_address;
230 }
231
232 is_unicast_ = !is_unicast_;
233}
234
235bool LinkMonitor::SendRequest() {
236 SLOG(Link, 2) << "In " << __func__ << ".";
237 if (!arp_client_.get()) {
238 if (!CreateClient()) {
239 LOG(ERROR) << "Failed to start ARP client.";
240 Stop();
Paul Stewartff845fc2012-08-07 07:28:44 -0700241 metrics_->NotifyLinkMonitorFailure(
Paul Stewart0443aa52012-08-09 10:43:50 -0700242 connection_->technology(),
243 Metrics::kLinkMonitorClientStartFailure,
244 0, 0, 0);
Paul Stewart6c72c972012-07-27 11:29:20 -0700245 return false;
246 }
247 } else if (AddMissedResponse()) {
248 // If an ARP client is still listening, this means we have timed
249 // out reception of the ARP reply.
250 return false;
251 } else {
252 // We already have an ArpClient instance running. These aren't
253 // bound sockets in the conventional sense, and we cannot distinguish
254 // which request (from which trial, or even from which component
255 // in the local system) an ARP reply was sent in response to.
256 // Therefore we keep the already open ArpClient in the case of
257 // a non-fatal timeout.
258 }
259
260 ByteString destination_mac_address(gateway_mac_address_.GetLength());
Paul Stewart9f7823e2012-08-09 10:58:26 -0700261 if (!IsGatewayFound()) {
Paul Stewart6c72c972012-07-27 11:29:20 -0700262 // The remote MAC addess is set by convention to be all-zeroes in the
263 // ARP header if not known. The ArpClient will translate an all-zeroes
264 // remote address into a send to the broadcast (all-ones) address in
265 // the Ethernet frame header.
266 SLOG_IF(Link, 2, is_unicast_) << "Sending broadcast since "
267 << "gateway MAC is unknown";
268 is_unicast_ = false;
269 } else if (is_unicast_) {
270 destination_mac_address = gateway_mac_address_;
271 }
272
273 ArpPacket request(connection_->local(), connection_->gateway(),
274 local_mac_address_, destination_mac_address);
275 if (!arp_client_->TransmitRequest(request)) {
276 LOG(ERROR) << "Failed to send ARP request. Stopping.";
277 Stop();
Paul Stewartff845fc2012-08-07 07:28:44 -0700278 metrics_->NotifyLinkMonitorFailure(
Paul Stewart0443aa52012-08-09 10:43:50 -0700279 connection_->technology(), Metrics::kLinkMonitorTransmitFailure,
280 0, 0, 0);
Paul Stewart6c72c972012-07-27 11:29:20 -0700281 return false;
282 }
283
284 time_->GetTimeMonotonic(&sent_request_at_);
285
286 dispatcher_->PostDelayedTask(send_request_callback_.callback(),
287 kTestPeriodMilliseconds);
288 return true;
289}
290
Paul Stewart3f43f432012-07-16 12:12:45 -0700291} // namespace shill