blob: 6bf1208add1a4444e824a303e6dc7e5a9a1b0ebb [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;
Paul Stewart036dba02012-08-07 12:34:41 -070031const char LinkMonitor::kDefaultLinkMonitorTechnologies[] = "wifi";
Paul Stewart6c72c972012-07-27 11:29:20 -070032const unsigned int LinkMonitor::kFailureThreshold = 5;
33const unsigned int LinkMonitor::kMaxResponseSampleFilterDepth = 5;
34
Paul Stewart3f43f432012-07-16 12:12:45 -070035LinkMonitor::LinkMonitor(const ConnectionRefPtr &connection,
36 EventDispatcher *dispatcher,
Paul Stewartff845fc2012-08-07 07:28:44 -070037 Metrics *metrics,
Paul Stewart6c72c972012-07-27 11:29:20 -070038 DeviceInfo *device_info,
Paul Stewart3f43f432012-07-16 12:12:45 -070039 const FailureCallback &failure_callback)
40 : connection_(connection),
41 dispatcher_(dispatcher),
Paul Stewartff845fc2012-08-07 07:28:44 -070042 metrics_(metrics),
Paul Stewart6c72c972012-07-27 11:29:20 -070043 device_info_(device_info),
Paul Stewart3f43f432012-07-16 12:12:45 -070044 failure_callback_(failure_callback),
Paul Stewart6c72c972012-07-27 11:29:20 -070045 broadcast_failure_count_(0),
46 unicast_failure_count_(0),
47 is_unicast_(false),
48 response_sample_count_(0),
49 response_sample_bucket_(0),
50 time_(Time::GetInstance()) {}
Paul Stewart3f43f432012-07-16 12:12:45 -070051
Paul Stewart6c72c972012-07-27 11:29:20 -070052LinkMonitor::~LinkMonitor() {
53 Stop();
54}
Paul Stewart3f43f432012-07-16 12:12:45 -070055
56bool LinkMonitor::Start() {
Paul Stewart6c72c972012-07-27 11:29:20 -070057 Stop();
58
59 if (!device_info_->GetMACAddress(
60 connection_->interface_index(), &local_mac_address_)) {
61 LOG(ERROR) << "Could not get local MAC address.";
Paul Stewartff845fc2012-08-07 07:28:44 -070062 metrics_->NotifyLinkMonitorFailure(
Paul Stewart0443aa52012-08-09 10:43:50 -070063 connection_->technology(),
64 Metrics::kLinkMonitorMacAddressNotFound,
65 0, 0, 0);
Paul Stewart6c72c972012-07-27 11:29:20 -070066 Stop();
67 return false;
68 }
69 gateway_mac_address_ = ByteString(local_mac_address_.GetLength());
70 send_request_callback_.Reset(
71 Bind(&LinkMonitor::SendRequestTask, Unretained(this)));
Paul Stewart0443aa52012-08-09 10:43:50 -070072 time_->GetTimeMonotonic(&started_monitoring_at_);
Paul Stewart6c72c972012-07-27 11:29:20 -070073 return SendRequest();
Paul Stewart3f43f432012-07-16 12:12:45 -070074}
75
76void LinkMonitor::Stop() {
Paul Stewart6c72c972012-07-27 11:29:20 -070077 SLOG(Link, 2) << "In " << __func__ << ".";
78 local_mac_address_.Clear();
79 gateway_mac_address_.Clear();
80 arp_client_.reset();
81 broadcast_failure_count_ = 0;
82 unicast_failure_count_ = 0;
83 is_unicast_ = false;
84 response_sample_bucket_ = 0;
85 response_sample_count_ = 0;
86 receive_response_handler_.reset();
87 send_request_callback_.Cancel();
Paul Stewart0443aa52012-08-09 10:43:50 -070088 timerclear(&started_monitoring_at_);
Paul Stewart6c72c972012-07-27 11:29:20 -070089 timerclear(&sent_request_at_);
90}
91
Paul Stewart9f7823e2012-08-09 10:58:26 -070092unsigned int LinkMonitor::GetResponseTimeMilliseconds() const {
Paul Stewart6c72c972012-07-27 11:29:20 -070093 return response_sample_count_ ?
94 response_sample_bucket_ / response_sample_count_ : 0;
95}
96
97void LinkMonitor::AddResponseTimeSample(
98 unsigned int response_time_milliseconds) {
99 SLOG(Link, 2) << "In " << __func__ << " with sample "
100 << response_time_milliseconds << ".";
Paul Stewartff845fc2012-08-07 07:28:44 -0700101 metrics_->NotifyLinkMonitorResponseTimeSampleAdded(
102 connection_->technology(), response_time_milliseconds);
Paul Stewart6c72c972012-07-27 11:29:20 -0700103 response_sample_bucket_ += response_time_milliseconds;
104 if (response_sample_count_ < kMaxResponseSampleFilterDepth) {
105 ++response_sample_count_;
106 } else {
107 response_sample_bucket_ =
108 response_sample_bucket_ * kMaxResponseSampleFilterDepth /
109 (kMaxResponseSampleFilterDepth + 1);
110 }
111}
112
113// static
114string LinkMonitor::HardwareAddressToString(const ByteString &address) {
115 std::vector<string> address_parts;
116 for (size_t i = 0; i < address.GetLength(); ++i) {
117 address_parts.push_back(
118 base::StringPrintf("%02x", address.GetConstData()[i]));
119 }
120 return JoinString(address_parts, ':');
121}
122
123bool LinkMonitor::CreateClient() {
124 arp_client_.reset(new ArpClient(connection_->interface_index()));
125
126 if (!arp_client_->Start()) {
Paul Stewartff845fc2012-08-07 07:28:44 -0700127 return false;
Paul Stewart6c72c972012-07-27 11:29:20 -0700128 }
Paul Stewart9f7823e2012-08-09 10:58:26 -0700129 SLOG(Link, 4) << "Created ARP client; listening on socket "
130 << arp_client_->socket() << ".";
Paul Stewart6c72c972012-07-27 11:29:20 -0700131 receive_response_handler_.reset(
132 dispatcher_->CreateReadyHandler(
133 arp_client_->socket(),
134 IOHandler::kModeInput,
135 Bind(&LinkMonitor::ReceiveResponse, Unretained(this))));
136 return true;
137}
138
139bool LinkMonitor::AddMissedResponse() {
140 SLOG(Link, 2) << "In " << __func__ << ".";
141 AddResponseTimeSample(kTestPeriodMilliseconds);
142
143 if (is_unicast_) {
144 ++unicast_failure_count_;
145 } else {
146 ++broadcast_failure_count_;
147 }
148
149 if (unicast_failure_count_ + broadcast_failure_count_ >= kFailureThreshold) {
150 LOG(ERROR) << "Link monitor has reached the failure threshold with "
151 << broadcast_failure_count_
152 << " broadcast failures and "
153 << unicast_failure_count_
154 << " unicast failures.";
155 failure_callback_.Run();
Paul Stewart0443aa52012-08-09 10:43:50 -0700156
157 struct timeval now, elapsed_time;
158 time_->GetTimeMonotonic(&now);
159 timersub(&now, &started_monitoring_at_, &elapsed_time);
160
Paul Stewartff845fc2012-08-07 07:28:44 -0700161 metrics_->NotifyLinkMonitorFailure(
162 connection_->technology(),
Paul Stewart0443aa52012-08-09 10:43:50 -0700163 Metrics::kLinkMonitorFailureThresholdReached,
164 elapsed_time.tv_sec,
165 broadcast_failure_count_,
166 unicast_failure_count_);
167
168 Stop();
Paul Stewart6c72c972012-07-27 11:29:20 -0700169 return true;
170 }
171 is_unicast_ = !is_unicast_;
172 return false;
173}
174
Paul Stewart9f7823e2012-08-09 10:58:26 -0700175bool LinkMonitor::IsGatewayFound() const {
176 return !gateway_mac_address_.IsZero();
177}
178
Paul Stewart6c72c972012-07-27 11:29:20 -0700179void LinkMonitor::ReceiveResponse(int fd) {
180 SLOG(Link, 2) << "In " << __func__ << ".";
181 ArpPacket packet;
182 ByteString sender;
183 if (!arp_client_->ReceiveReply(&packet, &sender)) {
184 return;
185 }
186
Paul Stewart9f7823e2012-08-09 10:58:26 -0700187 if (!connection_->local().address().Equals(
188 packet.remote_ip_address().address())) {
Paul Stewart6c72c972012-07-27 11:29:20 -0700189 SLOG(Link, 4) << "Response is not for our IP address.";
190 return;
191 }
192
Paul Stewart9f7823e2012-08-09 10:58:26 -0700193 if (!local_mac_address_.Equals(packet.remote_mac_address())) {
Paul Stewart6c72c972012-07-27 11:29:20 -0700194 SLOG(Link, 4) << "Response is not for our MAC address.";
195 return;
196 }
197
Paul Stewart9f7823e2012-08-09 10:58:26 -0700198 if (!connection_->gateway().address().Equals(
199 packet.local_ip_address().address())) {
Paul Stewart6c72c972012-07-27 11:29:20 -0700200 SLOG(Link, 4) << "Response is not from the gateway IP address.";
201 return;
202 }
203
204 struct timeval now, elapsed_time;
205 time_->GetTimeMonotonic(&now);
206 timersub(&now, &sent_request_at_, &elapsed_time);
207
208 AddResponseTimeSample(elapsed_time.tv_sec * 1000 +
209 elapsed_time.tv_usec / 1000);
210
211 receive_response_handler_.reset();
212 arp_client_.reset();
213
214 if (is_unicast_) {
215 unicast_failure_count_ = 0;
216 } else {
217 broadcast_failure_count_ = 0;
218 }
219
Paul Stewart9f7823e2012-08-09 10:58:26 -0700220 if (!gateway_mac_address_.Equals(packet.local_mac_address())) {
221 const ByteString &new_mac_address = packet.local_mac_address();
222 if (!IsGatewayFound()) {
Paul Stewart6c72c972012-07-27 11:29:20 -0700223 SLOG(Link, 2) << "Found gateway at "
224 << HardwareAddressToString(new_mac_address);
225 } else {
226 SLOG(Link, 2) << "Gateway MAC address changed.";
227 }
228 gateway_mac_address_ = new_mac_address;
229 }
230
231 is_unicast_ = !is_unicast_;
232}
233
234bool LinkMonitor::SendRequest() {
235 SLOG(Link, 2) << "In " << __func__ << ".";
236 if (!arp_client_.get()) {
237 if (!CreateClient()) {
238 LOG(ERROR) << "Failed to start ARP client.";
239 Stop();
Paul Stewartff845fc2012-08-07 07:28:44 -0700240 metrics_->NotifyLinkMonitorFailure(
Paul Stewart0443aa52012-08-09 10:43:50 -0700241 connection_->technology(),
242 Metrics::kLinkMonitorClientStartFailure,
243 0, 0, 0);
Paul Stewart6c72c972012-07-27 11:29:20 -0700244 return false;
245 }
246 } else if (AddMissedResponse()) {
247 // If an ARP client is still listening, this means we have timed
248 // out reception of the ARP reply.
249 return false;
250 } else {
251 // We already have an ArpClient instance running. These aren't
252 // bound sockets in the conventional sense, and we cannot distinguish
253 // which request (from which trial, or even from which component
254 // in the local system) an ARP reply was sent in response to.
255 // Therefore we keep the already open ArpClient in the case of
256 // a non-fatal timeout.
257 }
258
259 ByteString destination_mac_address(gateway_mac_address_.GetLength());
Paul Stewart9f7823e2012-08-09 10:58:26 -0700260 if (!IsGatewayFound()) {
Paul Stewart6c72c972012-07-27 11:29:20 -0700261 // The remote MAC addess is set by convention to be all-zeroes in the
262 // ARP header if not known. The ArpClient will translate an all-zeroes
263 // remote address into a send to the broadcast (all-ones) address in
264 // the Ethernet frame header.
265 SLOG_IF(Link, 2, is_unicast_) << "Sending broadcast since "
266 << "gateway MAC is unknown";
267 is_unicast_ = false;
268 } else if (is_unicast_) {
269 destination_mac_address = gateway_mac_address_;
270 }
271
272 ArpPacket request(connection_->local(), connection_->gateway(),
273 local_mac_address_, destination_mac_address);
274 if (!arp_client_->TransmitRequest(request)) {
275 LOG(ERROR) << "Failed to send ARP request. Stopping.";
276 Stop();
Paul Stewartff845fc2012-08-07 07:28:44 -0700277 metrics_->NotifyLinkMonitorFailure(
Paul Stewart0443aa52012-08-09 10:43:50 -0700278 connection_->technology(), Metrics::kLinkMonitorTransmitFailure,
279 0, 0, 0);
Paul Stewart6c72c972012-07-27 11:29:20 -0700280 return false;
281 }
282
283 time_->GetTimeMonotonic(&sent_request_at_);
284
285 dispatcher_->PostDelayedTask(send_request_callback_.callback(),
286 kTestPeriodMilliseconds);
287 return true;
288}
289
290void LinkMonitor::SendRequestTask() {
291 SendRequest();
Paul Stewart3f43f432012-07-16 12:12:45 -0700292}
293
294} // namespace shill