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 44 namespace shill { 45 46 class EventDispatcher; 47 class 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. 55 class 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 IsStarted()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