Home | History | Annotate | Download | only in shill
      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 
     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