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 
35 namespace shill {
36 
37 class AsyncConnection;
38 class DNSClient;
39 class DNSClientFactory;
40 class Error;
41 class EventDispatcher;
42 class IPAddress;
43 class IPAddressStore;
44 class 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)
54 class 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.
remote_ips()105   const IPAddressStore* remote_ips() const { return remote_ips_; }
106   virtual bool health_check_in_progress() const;
107 
108  protected:
109   // For unit-tests.
set_dispatcher(EventDispatcher * dispatcher)110   void set_dispatcher(EventDispatcher* dispatcher) {
111     dispatcher_ = dispatcher;
112   }
set_sock_fd(int sock_fd)113   void set_sock_fd(int sock_fd) { sock_fd_ = sock_fd; }
num_connection_failures()114   int16_t num_connection_failures() const { return num_connection_failures_; }
set_num_connection_failures(int16_t val)115   void set_num_connection_failures(int16_t val) {
116     num_connection_failures_ = val;
117   }
num_tx_queue_polling_attempts()118   int16_t num_tx_queue_polling_attempts() const {
119     return num_tx_queue_polling_attempts_;
120   }
set_num_tx_queue_polling_attempts(int16_t val)121   void set_num_tx_queue_polling_attempts(int16_t val) {
122     num_tx_queue_polling_attempts_ = val;
123   }
num_congested_queue_detected()124   int16_t num_congested_queue_detected() const {
125     return num_congested_queue_detected_;
126   }
set_num_congested_queue_detected(int16_t val)127   void set_num_congested_queue_detected(int16_t val) {
128     num_congested_queue_detected_ = val;
129   }
num_successful_sends()130   int16_t num_successful_sends() const { return num_successful_sends_; }
set_num_successful_sends(int16_t val)131   void set_num_successful_sends(int16_t val) {
132     num_successful_sends_ = val;
133   }
set_old_transmit_queue_value(uint64_t val)134   void set_old_transmit_queue_value(uint64_t val) {
135     old_transmit_queue_value_ = val;
136   }
health_check_result()137   Result health_check_result() const { return health_check_result_; }
tcp_connection()138   AsyncConnection* tcp_connection() const { return tcp_connection_.get(); }
connection()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