1//
2// Copyright (C) 2013 The Android Open Source Project
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8//      http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15//
16
17#ifndef SHILL_CONNECTION_HEALTH_CHECKER_H_
18#define SHILL_CONNECTION_HEALTH_CHECKER_H_
19
20#include <memory>
21#include <string>
22#include <vector>
23
24#include <base/callback.h>
25#include <base/cancelable_callback.h>
26#include <base/macros.h>
27#include <base/memory/scoped_vector.h>
28#include <base/memory/weak_ptr.h>
29#include <gtest/gtest_prod.h>
30
31#include "shill/net/sockets.h"
32#include "shill/refptr_types.h"
33#include "shill/socket_info.h"
34
35namespace shill {
36
37class AsyncConnection;
38class DNSClient;
39class DNSClientFactory;
40class Error;
41class EventDispatcher;
42class IPAddress;
43class IPAddressStore;
44class SocketInfoReader;
45
46// The ConnectionHealthChecker class implements the facilities to test
47// connectivity status on some connection asynchronously.
48// In particular, the class can distinguish between three states of the
49// connection:
50//   -(1)- No connectivity (TCP connection can not be established)
51//   -(2)- Partial connectivity (TCP connection can be established, but no data
52//         transfer)
53//   -(3)- Connectivity OK (TCP connection established, is healthy)
54class ConnectionHealthChecker {
55 public:
56  enum Result {
57    // There was some problem in the setup of ConnctionHealthChecker.
58    // Could not attempt a tcp connection.
59    kResultUnknown,
60    // Failed to create TCP connection. Condition -(1)-.
61    kResultConnectionFailure,
62    // Failed to send data on TCP connection. Condition -(2)-.
63    kResultCongestedTxQueue,
64    // Condition -(3)-.
65    kResultSuccess
66  };
67
68  ConnectionHealthChecker(ConnectionRefPtr connection,
69                          EventDispatcher* dispatcher,
70                          IPAddressStore* remote_ips,
71                          const base::Callback<void(Result)>& result_callback);
72  virtual ~ConnectionHealthChecker();
73
74  // A new ConnectionHealthChecker is created with a default URL to attempt the
75  // TCP connection with. Add a URL to try.
76  virtual void AddRemoteURL(const std::string& url_string);
77
78  // Name resolution can fail in conditions -(1)- and -(2)-. Add an IP address
79  // to attempt the TCP connection with.
80  virtual void AddRemoteIP(IPAddress ip);
81
82  // Change the associated Connection on the Device.
83  // This will restart any ongoing health check. Any ongoing DNS query will be
84  // dropped (not restarted).
85  virtual void SetConnection(ConnectionRefPtr connection);
86
87  // Start a connection health check. The health check involves one or more
88  // attempts at establishing and using a TCP connection. |result_callback_| is
89  // called with the final result of the check. |result_callback_| will always
90  // be called after a call to Start() unless Stop() is called in the meantime.
91  // |result_callback_| may be called before Start() completes.
92  //
93  // Calling Start() while a health check is in progress is a no-op.
94  virtual void Start();
95
96  // Stop the current health check. No callback is called as a side effect of
97  // this function.
98  //
99  // Calling Stop() on a Stop()ed health check is a no-op.
100  virtual void Stop();
101
102  static const char* ResultToString(Result result);
103
104  // Accessors.
105  const IPAddressStore* remote_ips() const { return remote_ips_; }
106  virtual bool health_check_in_progress() const;
107
108 protected:
109  // For unit-tests.
110  void set_dispatcher(EventDispatcher* dispatcher) {
111    dispatcher_ = dispatcher;
112  }
113  void set_sock_fd(int sock_fd) { sock_fd_ = sock_fd; }
114  int16_t num_connection_failures() const { return num_connection_failures_; }
115  void set_num_connection_failures(int16_t val) {
116    num_connection_failures_ = val;
117  }
118  int16_t num_tx_queue_polling_attempts() const {
119    return num_tx_queue_polling_attempts_;
120  }
121  void set_num_tx_queue_polling_attempts(int16_t val) {
122    num_tx_queue_polling_attempts_ = val;
123  }
124  int16_t num_congested_queue_detected() const {
125    return num_congested_queue_detected_;
126  }
127  void set_num_congested_queue_detected(int16_t val) {
128    num_congested_queue_detected_ = val;
129  }
130  int16_t num_successful_sends() const { return num_successful_sends_; }
131  void set_num_successful_sends(int16_t val) {
132    num_successful_sends_ = val;
133  }
134  void set_old_transmit_queue_value(uint64_t val) {
135    old_transmit_queue_value_ = val;
136  }
137  Result health_check_result() const { return health_check_result_; }
138  AsyncConnection* tcp_connection() const { return tcp_connection_.get(); }
139  Connection* connection() const { return connection_.get(); }
140
141 private:
142  friend class ConnectionHealthCheckerTest;
143  FRIEND_TEST(ConnectionHealthCheckerTest, GarbageCollectDNSClients);
144  FRIEND_TEST(ConnectionHealthCheckerTest, GetSocketInfo);
145  FRIEND_TEST(ConnectionHealthCheckerTest, NextHealthCheckSample);
146  FRIEND_TEST(ConnectionHealthCheckerTest, OnConnectionComplete);
147  FRIEND_TEST(ConnectionHealthCheckerTest, SetConnection);
148  FRIEND_TEST(ConnectionHealthCheckerTest, VerifySentData);
149
150  // List of static IPs for connection health check.
151  static const char* kDefaultRemoteIPPool[];
152  // Time to wait for DNS server.
153  static const int kDNSTimeoutMilliseconds;
154  static const int kInvalidSocket;
155  // After |kMaxFailedConnectionAttempts| failed attempts to connect, give up
156  // health check and return failure.
157  static const int kMaxFailedConnectionAttempts;
158  // After sending a small amount of data, attempt |kMaxSentDataPollingAttempts|
159  // times to see if the data was sent successfully.
160  static const int kMaxSentDataPollingAttempts;
161  // After |kMinCongestedQueueAttempts| to send data indicate a congested tx
162  // queue, finish health check and report a congested queue.
163  static const int kMinCongestedQueueAttempts;
164  // After sending data |kMinSuccessfulAttempts| times succesfully, finish
165  // health check and report a healthy connection.
166  static const int kMinSuccessfulSendAttempts;
167  // Number of DNS queries to be spawned when a new remote URL is added.
168  static const int kNumDNSQueries;
169  static const uint16_t kRemotePort;
170  // Time to wait before testing successful data transfer / disconnect after
171  // request is made on the device.
172  static const int kTCPStateUpdateWaitMilliseconds;
173
174  // Callback for DnsClient
175  void GetDNSResult(const Error& error, const IPAddress& ip);
176  void GarbageCollectDNSClients();
177
178  // Start a new AsyncConnection with callback set to OnConnectionComplete().
179  void NextHealthCheckSample();
180  void ReportResult();
181
182  // Callback for AsyncConnection.
183  // Observe the setup connection to test health state
184  void OnConnectionComplete(bool success, int sock_fd);
185
186  void VerifySentData();
187  bool GetSocketInfo(int sock_fd, SocketInfo* sock_info);
188
189  void SetSocketDescriptor(int sock_fd);
190  void ClearSocketDescriptor();
191
192  // The connection on which the health check is being run.
193  ConnectionRefPtr connection_;
194  EventDispatcher* dispatcher_;
195  // Set of IPs to create TCP connection with for the health check.
196  IPAddressStore* remote_ips_;
197  base::Callback<void(Result)> result_callback_;
198
199  std::unique_ptr<Sockets> socket_;
200  base::WeakPtrFactory<ConnectionHealthChecker> weak_ptr_factory_;
201
202  // Callback passed to |tcp_connection_| to report an established TCP
203  // connection.
204  const base::Callback<void(bool, int)> connection_complete_callback_;
205  // Active TCP connection during health check.
206  std::unique_ptr<AsyncConnection> tcp_connection_;
207  const base::Callback<void(void)> report_result_;
208  // Active socket for |tcp_connection_| during an active health check.
209  int sock_fd_;
210  // Interface to read TCP connection information from the system.
211  std::unique_ptr<SocketInfoReader> socket_info_reader_;
212
213  DNSClientFactory* dns_client_factory_;
214  ScopedVector<DNSClient> dns_clients_;
215  const base::Callback<void(const Error&, const IPAddress&)>
216      dns_client_callback_;
217
218  // Store the old value of the transmit queue to verify that data sent on the
219  // connection is actually transmitted.
220  uint64_t old_transmit_queue_value_;
221  // Callback to post a delayed check on whether data sent on the TCP connection
222  // was successfully transmitted.
223  base::CancelableClosure verify_sent_data_callback_;
224
225  bool health_check_in_progress_;
226  // Number of connection failures in currently active health check.
227  int16_t num_connection_failures_;
228  // Number of times we have checked the tx-queue for the current send attempt.
229  int16_t num_tx_queue_polling_attempts_;
230  // Number of out of credit scenarios detected in current health check.
231  int16_t num_congested_queue_detected_;
232  // Number of successful send attempts currently active health check.
233  int16_t num_successful_sends_;
234
235  // Snooze time while polling for updated /proc/tcpinfo
236  int tcp_state_update_wait_milliseconds_;
237
238  // Temporarily store the result of health check so that |report_result_|
239  // can report it.
240  Result health_check_result_;
241
242  DISALLOW_COPY_AND_ASSIGN(ConnectionHealthChecker);
243};
244
245}  // namespace shill
246
247#endif  // SHILL_CONNECTION_HEALTH_CHECKER_H_
248