1//
2// Copyright (C) 2015 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_ICMP_SESSION_H_
18#define SHILL_ICMP_SESSION_H_
19
20#if defined(__ANDROID__)
21#include <linux/icmp.h>
22#else
23#include <netinet/ip_icmp.h>
24#endif  // __ANDROID__
25
26#include <map>
27#include <memory>
28#include <set>
29#include <string>
30#include <utility>
31#include <vector>
32
33#include <base/callback.h>
34#include <base/cancelable_callback.h>
35#include <base/macros.h>
36#include <base/memory/weak_ptr.h>
37#include <base/time/default_tick_clock.h>
38#include <base/time/tick_clock.h>
39#include <gtest/gtest_prod.h>  // for FRIEND_TEST
40
41#include "shill/icmp.h"
42#include "shill/net/io_handler.h"
43
44namespace shill {
45
46class EventDispatcher;
47class IPAddress;
48
49// The IcmpSession class encapsulates the task of performing a stateful exchange
50// of echo requests and echo replies between this host and another (i.e. ping).
51// The Icmp class is used to perform the sending of echo requests. Each
52// IcmpSession object only allows one ICMP session to be running at one time.
53// Multiple ICMP sessions can be run concurrently by creating multiple
54// IcmpSession objects.
55class IcmpSession {
56 public:
57  // The result of an ICMP session is a vector of time deltas representing how
58  // long it took to receive a echo reply for each sent echo request. The vector
59  // is sorted in the order that the echo requests were sent. Zero time deltas
60  // represent echo requests that we did not receive a corresponding reply for.
61  using IcmpSessionResult = std::vector<base::TimeDelta>;
62  using IcmpSessionResultCallback =
63      base::Callback<void(const IcmpSessionResult&)>;
64
65  explicit IcmpSession(EventDispatcher* dispatcher);
66
67  // We always call IcmpSession::Stop in the destructor to clean up, in case an
68  // ICMP session is still in progress.
69  virtual ~IcmpSession();
70
71  // Starts an ICMP session, sending |kNumEchoRequestsToSend| echo requests to
72  // |destination|, |kEchoRequestIntervalSeconds| apart. |result_callback| will
73  // be called a) after all echo requests are sent and all echo replies are
74  // received, or b) after |kTimeoutSeconds| have passed. |result_callback| will
75  // only be invoked once on the first occurrence of either of these events.
76  virtual bool Start(const IPAddress& destination,
77                     const IcmpSessionResultCallback& result_callback);
78
79  // Stops the current ICMP session by closing the ICMP socket and resetting
80  // callbacks. Does nothing if a ICMP session is not started.
81  virtual void Stop();
82
83  bool IsStarted() { return icmp_->IsStarted(); }
84
85  // Utility function that returns false iff |result| indicates that no echo
86  // replies were received to any ICMP echo request that was sent during the
87  // ICMP session that generated |result|.
88  static bool AnyRepliesReceived(const IcmpSessionResult& result);
89
90  // Utility function that returns the packet loss rate for the ICMP session
91  // that generated |result| is greater than |percentage_threshold| percent.
92  // The percentage packet loss determined by this function will be rounded
93  // down to the closest integer percentage value. |percentage_threshold| is
94  // expected to be a non-negative integer value.
95  static bool IsPacketLossPercentageGreaterThan(const IcmpSessionResult& result,
96                                                int percentage_threshold);
97
98 private:
99  using SentRecvTimePair = std::pair<base::TimeTicks, base::TimeTicks>;
100
101  friend class IcmpSessionTest;
102
103  FRIEND_TEST(IcmpSessionTest, Constructor);  // for |echo_id_|
104
105  static uint16_t kNextUniqueEchoId;  // unique across IcmpSession objects
106  static const int kTotalNumEchoRequests;
107  static const int kEchoRequestIntervalSeconds;
108  static const size_t kTimeoutSeconds;
109
110  // Sends a single echo request to |destination|. This function will call
111  // itself repeatedly via the event loop every |kEchoRequestIntervalSeconds|
112  // until |kNumEchoRequestToSend| echo requests are sent or the timeout is
113  // reached.
114  void TransmitEchoRequestTask(const IPAddress& destination);
115
116  // Called when an ICMP packet is received.
117  void OnEchoReplyReceived(InputData* data);
118
119  // Helper function that generates the result of the current ICMP session.
120  IcmpSessionResult GenerateIcmpResult();
121
122  // Called when the input handler |echo_reply_handler_| encounters an error.
123  void OnEchoReplyError(const std::string& error_msg);
124
125  // Calls |result_callback_| with the results collected so far, then stops the
126  // IcmpSession. This function is called when the ICMP session successfully
127  // completes, or when it times out. Does nothing if an ICMP session is not
128  // started.
129  void ReportResultAndStopSession();
130
131  base::WeakPtrFactory<IcmpSession> weak_ptr_factory_;
132  EventDispatcher* dispatcher_;
133  std::unique_ptr<Icmp> icmp_;
134  const uint16_t echo_id_;  // unique ID for this object's echo request/replies
135  uint16_t current_sequence_number_;
136  std::map<uint16_t, SentRecvTimePair> seq_num_to_sent_recv_time_;
137  std::set<uint16_t> received_echo_reply_seq_numbers_;
138  // Allow for an injectable tick clock for testing.
139  base::TickClock* tick_clock_;
140  base::DefaultTickClock default_tick_clock_;
141  base::CancelableClosure timeout_callback_;
142  IcmpSessionResultCallback result_callback_;
143  IOHandler::InputCallback echo_reply_callback_;
144  std::unique_ptr<IOHandler> echo_reply_handler_;
145
146  DISALLOW_COPY_AND_ASSIGN(IcmpSession);
147};
148
149}  // namespace shill
150
151#endif  // SHILL_ICMP_SESSION_H_
152