blob: 85644c0320ddea8aae10179d9d22641d709f8d57 [file] [log] [blame]
Peter Qiud7881022015-01-21 12:27:41 -08001// Copyright 2015 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/active_link_monitor.h"
6
7#include <string>
8#include <vector>
9
10#include <base/bind.h>
11#include <base/strings/stringprintf.h>
12#include <base/strings/string_util.h>
13
14#include "shill/arp_client.h"
15#include "shill/arp_packet.h"
16#include "shill/connection.h"
17#include "shill/device_info.h"
18#include "shill/event_dispatcher.h"
19#include "shill/logging.h"
20#include "shill/metrics.h"
21#include "shill/net/ip_address.h"
22#include "shill/net/shill_time.h"
23
24using base::Bind;
25using base::Unretained;
26using std::string;
27
28namespace shill {
29
30namespace Logging {
31static auto kModuleLogScope = ScopeLogger::kLink;
32static string ObjectID(Connection *c) { return c->interface_name(); }
33}
34
35const int ActiveLinkMonitor::kDefaultTestPeriodMilliseconds = 5000;
36const int ActiveLinkMonitor::kFailureThreshold = 5;
37const int ActiveLinkMonitor::kFastTestPeriodMilliseconds = 200;
38const int ActiveLinkMonitor::kMaxResponseSampleFilterDepth = 5;
39const int ActiveLinkMonitor::kUnicastReplyReliabilityThreshold = 10;
40
41ActiveLinkMonitor::ActiveLinkMonitor(const ConnectionRefPtr &connection,
42 EventDispatcher *dispatcher,
43 Metrics *metrics,
44 DeviceInfo *device_info,
45 const FailureCallback &failure_callback,
46 const SuccessCallback &success_callback)
47 : connection_(connection),
48 dispatcher_(dispatcher),
49 metrics_(metrics),
50 device_info_(device_info),
51 failure_callback_(failure_callback),
52 success_callback_(success_callback),
53 arp_client_(new ArpClient(connection->interface_index())),
54 test_period_milliseconds_(kDefaultTestPeriodMilliseconds),
55 broadcast_failure_count_(0),
56 unicast_failure_count_(0),
57 broadcast_success_count_(0),
58 unicast_success_count_(0),
59 is_unicast_(false),
60 gateway_supports_unicast_arp_(false),
61 response_sample_count_(0),
62 response_sample_bucket_(0),
63 time_(Time::GetInstance()) {
64}
65
66ActiveLinkMonitor::~ActiveLinkMonitor() {
67 Stop();
68}
69
70bool ActiveLinkMonitor::Start(int test_period) {
71 StopMonitorCycle();
72 return StartInternal(test_period);
73}
74
75void ActiveLinkMonitor::Stop() {
76 SLOG(connection_.get(), 2) << "In " << __func__ << ".";
77 // Stop current cycle.
78 StopMonitorCycle();
79
80 // Clear stats accumulated from previous monitor cycles.
81 local_mac_address_.Clear();
82 gateway_mac_address_.Clear();
83 broadcast_success_count_ = 0;
84 unicast_success_count_ = 0;
85 broadcast_failure_count_ = 0;
86 unicast_failure_count_ = 0;
87 is_unicast_ = false;
88 gateway_supports_unicast_arp_ = false;
89 response_sample_bucket_ = 0;
90 response_sample_count_ = 0;
91}
92
93int ActiveLinkMonitor::GetResponseTimeMilliseconds() const {
94 return response_sample_count_ ?
95 response_sample_bucket_ / response_sample_count_ : 0;
96}
97
98bool ActiveLinkMonitor::IsGatewayFound() const {
99 return !gateway_mac_address_.IsZero();
100}
101
102bool ActiveLinkMonitor::StartInternal(int probe_period_milliseconds) {
103 test_period_milliseconds_ = probe_period_milliseconds;
104 if (test_period_milliseconds_ > kDefaultTestPeriodMilliseconds) {
105 LOG(WARNING) << "Long test period; UMA stats will be truncated.";
106 }
107
108 if (!device_info_->GetMACAddress(
109 connection_->interface_index(), &local_mac_address_)) {
110 LOG(ERROR) << "Could not get local MAC address.";
111 metrics_->NotifyLinkMonitorFailure(
112 connection_->technology(),
113 Metrics::kLinkMonitorMacAddressNotFound,
114 0, 0, 0);
115 Stop();
116 return false;
117 }
118
119 if (!StartArpClient()) {
120 LOG(ERROR) << "Failed to start ARP client.";
121 metrics_->NotifyLinkMonitorFailure(
122 connection_->technology(),
123 Metrics::kLinkMonitorClientStartFailure,
124 0, 0, 0);
125 Stop();
126 return false;
127 }
128
129 if (gateway_mac_address_.IsEmpty()) {
130 gateway_mac_address_ = ByteString(local_mac_address_.GetLength());
131 }
132 send_request_callback_.Reset(
133 Bind(&ActiveLinkMonitor::SendRequest, Unretained(this)));
134 // Post a task to send ARP request instead of calling it synchronously, to
135 // maintain consistent expectation in the case of send failures, which will
136 // always invoke failure callback.
137 dispatcher_->PostTask(send_request_callback_.callback());
138 return true;
139}
140
141void ActiveLinkMonitor::StopMonitorCycle() {
142 StopArpClient();
143 send_request_callback_.Cancel();
144 timerclear(&sent_request_at_);
145}
146
147void ActiveLinkMonitor::AddResponseTimeSample(int response_time_milliseconds) {
148 SLOG(connection_.get(), 2) << "In " << __func__ << " with sample "
149 << response_time_milliseconds << ".";
150 metrics_->NotifyLinkMonitorResponseTimeSampleAdded(
151 connection_->technology(), response_time_milliseconds);
152 response_sample_bucket_ += response_time_milliseconds;
153 if (response_sample_count_ < kMaxResponseSampleFilterDepth) {
154 ++response_sample_count_;
155 } else {
156 response_sample_bucket_ =
157 response_sample_bucket_ * kMaxResponseSampleFilterDepth /
158 (kMaxResponseSampleFilterDepth + 1);
159 }
160}
161
162// static
163string ActiveLinkMonitor::HardwareAddressToString(const ByteString &address) {
164 std::vector<string> address_parts;
165 for (size_t i = 0; i < address.GetLength(); ++i) {
166 address_parts.push_back(
167 base::StringPrintf("%02x", address.GetConstData()[i]));
168 }
169 return JoinString(address_parts, ':');
170}
171
172bool ActiveLinkMonitor::StartArpClient() {
173 if (!arp_client_->StartReplyListener()) {
174 return false;
175 }
176 SLOG(connection_.get(), 4) << "Created ARP client; listening on socket "
177 << arp_client_->socket() << ".";
178 receive_response_handler_.reset(
179 dispatcher_->CreateReadyHandler(
180 arp_client_->socket(),
181 IOHandler::kModeInput,
182 Bind(&ActiveLinkMonitor::ReceiveResponse, Unretained(this))));
183 return true;
184}
185
186void ActiveLinkMonitor::StopArpClient() {
187 arp_client_->Stop();
188 receive_response_handler_.reset();
189}
190
191bool ActiveLinkMonitor::AddMissedResponse() {
192 SLOG(connection_.get(), 2) << "In " << __func__ << ".";
193 AddResponseTimeSample(test_period_milliseconds_);
194
195 if (is_unicast_) {
196 if (gateway_supports_unicast_arp_) {
197 ++unicast_failure_count_;
198 }
199 unicast_success_count_ = 0;
200 } else {
201 ++broadcast_failure_count_;
202 broadcast_success_count_ = 0;
203 }
204
205 if (unicast_failure_count_ + broadcast_failure_count_ >= kFailureThreshold) {
206 LOG(ERROR) << "Link monitor has reached the failure threshold with "
207 << broadcast_failure_count_
208 << " broadcast failures and "
209 << unicast_failure_count_
210 << " unicast failures.";
211 failure_callback_.Run(Metrics::kLinkMonitorFailureThresholdReached,
212 broadcast_failure_count_,
213 unicast_failure_count_);
214 Stop();
215 return true;
216 }
217 is_unicast_ = !is_unicast_;
218 return false;
219}
220
221void ActiveLinkMonitor::ReceiveResponse(int fd) {
222 SLOG(connection_.get(), 2) << "In " << __func__ << ".";
223 ArpPacket packet;
224 ByteString sender;
225 if (!arp_client_->ReceivePacket(&packet, &sender)) {
226 return;
227 }
228
229 if (!packet.IsReply()) {
230 SLOG(connection_.get(), 4) << "This is not a reply packet. Ignoring.";
231 return;
232 }
233
234 if (!connection_->local().address().Equals(
235 packet.remote_ip_address().address())) {
236 SLOG(connection_.get(), 4) << "Response is not for our IP address.";
237 return;
238 }
239
240 if (!local_mac_address_.Equals(packet.remote_mac_address())) {
241 SLOG(connection_.get(), 4) << "Response is not for our MAC address.";
242 return;
243 }
244
245 if (!connection_->gateway().address().Equals(
246 packet.local_ip_address().address())) {
247 SLOG(connection_.get(), 4)
248 << "Response is not from the gateway IP address.";
249 return;
250 }
251
252 struct timeval now, elapsed_time;
253 time_->GetTimeMonotonic(&now);
254 timersub(&now, &sent_request_at_, &elapsed_time);
255
256 AddResponseTimeSample(elapsed_time.tv_sec * 1000 +
257 elapsed_time.tv_usec / 1000);
258
259 if (is_unicast_) {
260 ++unicast_success_count_;
261 unicast_failure_count_ = 0;
262 if (unicast_success_count_ >= kUnicastReplyReliabilityThreshold) {
263 SLOG_IF(Link, 2, !gateway_supports_unicast_arp_)
264 << "Gateway is now considered a reliable unicast responder. "
265 "Unicast failures will now count.";
266 gateway_supports_unicast_arp_ = true;
267 }
268 } else {
269 ++broadcast_success_count_;
270 broadcast_failure_count_ = 0;
271 }
272
273 if (!gateway_mac_address_.Equals(packet.local_mac_address())) {
274 const ByteString &new_mac_address = packet.local_mac_address();
275 if (!IsGatewayFound()) {
276 SLOG(connection_.get(), 2) << "Found gateway at "
277 << HardwareAddressToString(new_mac_address);
278 } else {
279 SLOG(connection_.get(), 2) << "Gateway MAC address changed.";
280 }
281 gateway_mac_address_ = new_mac_address;
282 }
283
284 is_unicast_ = !is_unicast_;
285
286 // Stop the current cycle, and invoke the success callback. All the
287 // accumulated stats regarding the gateway are not cleared.
288 StopMonitorCycle();
289 success_callback_.Run();
290}
291
292void ActiveLinkMonitor::SendRequest() {
293 SLOG(connection_.get(), 2) << "In " << __func__ << ".";
294
295 // Timeout waiting for ARP reply and exceed the failure threshold.
296 if (timerisset(&sent_request_at_) && AddMissedResponse()) {
297 return;
298 }
299
300 ByteString destination_mac_address(gateway_mac_address_.GetLength());
301 if (!IsGatewayFound()) {
302 // The remote MAC addess is set by convention to be all-zeroes in the
303 // ARP header if not known. The ArpClient will translate an all-zeroes
304 // remote address into a send to the broadcast (all-ones) address in
305 // the Ethernet frame header.
306 SLOG_IF(Link, 2, is_unicast_) << "Sending broadcast since "
307 << "gateway MAC is unknown";
308 is_unicast_ = false;
309 } else if (is_unicast_) {
310 destination_mac_address = gateway_mac_address_;
311 }
312 LOG(INFO) << "IsGatway " << IsGatewayFound() << " unicast: " << is_unicast_;
313 ArpPacket request(connection_->local(), connection_->gateway(),
314 local_mac_address_, destination_mac_address);
315 if (!arp_client_->TransmitRequest(request)) {
316 LOG(ERROR) << "Failed to send ARP request. Stopping.";
317 failure_callback_.Run(Metrics::kLinkMonitorTransmitFailure,
318 broadcast_failure_count_,
319 unicast_failure_count_);
320 Stop();
321 return;
322 }
323
324 time_->GetTimeMonotonic(&sent_request_at_);
325
326 dispatcher_->PostDelayedTask(send_request_callback_.callback(),
327 test_period_milliseconds_);
328}
329
330} // namespace shill