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