Pinger now handles ICMP Destination Unreachable messages
authorGuilherme Maciel Ferreira <guilherme.maciel.ferreira@intra2net.com>
Fri, 8 Apr 2011 14:34:53 +0000 (16:34 +0200)
committerGuilherme Maciel Ferreira <guilherme.maciel.ferreira@intra2net.com>
Fri, 8 Apr 2011 14:34:53 +0000 (16:34 +0200)
- handle_receive_echo_reply rename to handle_receive_icmp_packet, once it handles not just echo replies
- timer member variable has a more meaningful name

src/ping/boostpinger.cpp
src/ping/boostpinger.h

index 26b4f4c..4d77213 100644 (file)
@@ -10,6 +10,7 @@
 #include "icmp/icmpdata.h"
 #include "icmp/icmpheader.h"
 #include "icmp/icmppacket.h"
+#include "icmp/icmptype.h"
 #include "icmp/ipv4header.h"
 
 using namespace std;
@@ -29,7 +30,7 @@ BoostPinger::BoostPinger(
     IoService( io_serv ),
     DestinationEndpoint(),
     Socket( io_serv, icmp::v4() ),
-    Timer( io_serv ),
+    IcmpPacketReceiveTimer( io_serv ),
     SequenceNumber( 0 ),
     TimeSent( microsec_clock::universal_time() ),
     ReplyBuffer(),
@@ -112,7 +113,7 @@ IcmpPacket BoostPinger::create_echo_request(
 {
     const IcmpData icmp_data( "ping-message" );
 
-    IcmpHeader::IcmpType type = IcmpHeader::IcmpType_EchoRequest;
+    IcmpType type = IcmpType_EchoRequest;
     uint8_t code = 0;
     uint16_t identifier = get_identifier();
     IcmpChecksumCalculator calculator( icmp_data.begin(), icmp_data.end() );
@@ -159,8 +160,10 @@ void BoostPinger::schedule_timeout_echo_reply()
 {
     // Wait up to N seconds for a reply.
     RepliesCount = 0;
-    (void) Timer.expires_at( TimeSent + seconds( EchoReplyTimeoutInSec ) );
-    Timer.async_wait(
+    (void) IcmpPacketReceiveTimer.expires_at(
+            TimeSent + seconds( EchoReplyTimeoutInSec )
+    );
+    IcmpPacketReceiveTimer.async_wait(
             boost::bind( &BoostPinger::handle_timeout_echo_reply, this )
     );
 }
@@ -181,8 +184,12 @@ void BoostPinger::schedule_next_echo_request()
 {
     // Requests must be sent no less than one second apart.
     const int echo_request_interval_in_sec = 1;
-    (void) Timer.expires_at( TimeSent + seconds( echo_request_interval_in_sec ) );
-    Timer.async_wait( boost::bind( &BoostPinger::start_send, this ) );
+    (void) IcmpPacketReceiveTimer.expires_at(
+            TimeSent + seconds( echo_request_interval_in_sec )
+    );
+    IcmpPacketReceiveTimer.async_wait(
+            boost::bind( &BoostPinger::start_send, this )
+    );
 }
 
 void BoostPinger::start_receive()
@@ -193,30 +200,35 @@ void BoostPinger::start_receive()
     // Wait for a reply. We prepare the buffer to receive up to 64KB.
     Socket.async_receive(
             ReplyBuffer.prepare( 65536 ),
-            boost::bind( &BoostPinger::handle_receive_echo_reply, this, _2 )
+            boost::bind( &BoostPinger::handle_receive_icmp_packet, this, _2 )
     );
 }
 
-void BoostPinger::handle_receive_echo_reply( const size_t &bytes_transferred )
+void BoostPinger::handle_receive_icmp_packet( const size_t &bytes_transferred )
 {
     // The actual number of bytes received is committed to the buffer so that we
     // can extract it using a std::istream object.
     ReplyBuffer.commit( bytes_transferred );
 
-    // Decode the reply packet.
     istream is( &ReplyBuffer );
+    if ( !is )
+        return;
+
+    // Decode the reply packet.
     IcmpPacket icmp_packet;
     is >> icmp_packet;
 
     // We can receive all ICMP packets received by the host, so we need to
     // filter out only the echo replies that match the our identifier and
     // expected sequence number.
-    if ( is && icmp_packet.match( IcmpHeader::IcmpType_EchoReply, get_identifier(), SequenceNumber ) )
+    if ( icmp_packet.match(
+            IcmpType_EchoReply, get_identifier(), SequenceNumber
+    ) )
     {
         // If this is the first reply, interrupt the echo reply timeout.
         if ( RepliesCount == 0 )
         {
-            (void) Timer.cancel();
+            (void) IcmpPacketReceiveTimer.cancel();
         }
 
         ++RepliesCount;
@@ -225,6 +237,20 @@ void BoostPinger::handle_receive_echo_reply( const size_t &bytes_transferred )
 
         set_ping_status( PingStatus_SuccessReply );
     }
+    else if ( icmp_packet.match(
+            IcmpType_DestinationUnreachable, get_identifier(), SequenceNumber
+    ) )
+    {
+        // If this is the first reply, interrupt the echo reply timeout.
+        if ( RepliesCount == 0 )
+        {
+            (void) IcmpPacketReceiveTimer.cancel();
+        }
+
+        print_destination_unreachable( icmp_packet );
+
+        set_ping_status( PingStatus_FailureDestinationUnreachable );
+    }
 
     start_receive();
 }
@@ -234,6 +260,8 @@ void BoostPinger::print_echo_reply(
         const size_t &bytes_transferred
 ) const
 {
+    BOOST_ASSERT( icmp_packet.get_icmp_header().get_type() == IcmpType_EchoReply );
+
     Ipv4Header ipv4_hdr = icmp_packet.get_ip_header();
     IcmpHeader icmp_hdr = icmp_packet.get_icmp_header();
 
@@ -251,6 +279,23 @@ void BoostPinger::print_echo_reply(
          << " time=" << time << " ms" << endl;
 }
 
+void BoostPinger::print_destination_unreachable(
+        const IcmpPacket &icmp_packet
+) const
+{
+    BOOST_ASSERT( icmp_packet.get_icmp_header().get_type() == IcmpType_DestinationUnreachable );
+
+    Ipv4Header ipv4_hdr = icmp_packet.get_ip_header();
+    IcmpHeader icmp_hdr = icmp_packet.get_icmp_header();
+
+    string local_address = ipv4_hdr.get_destination_address().to_string();
+    int sequence_number = icmp_hdr.get_sequence_number();
+
+    cout << "From " << local_address
+         << " icmp_seq=" << sequence_number
+         << " Destination Net Unreachable" << endl;
+}
+
 void BoostPinger::set_ping_status( BoostPinger::PingStatus ping_status )
 {
     PingerStatus = ping_status;
index 766378c..bb96daf 100644 (file)
@@ -30,7 +30,8 @@ private:
     {
         PingStatus_NotSent,
         PingStatus_SuccessReply,
-        PingStatus_FailureTimeout
+        PingStatus_FailureTimeout,
+        PingStatus_FailureDestinationUnreachable
     };
 
 private:
@@ -45,11 +46,14 @@ private:
     void schedule_next_echo_request();
 
     void start_receive();
-    void handle_receive_echo_reply( const std::size_t &bytes_transferred );
+    void handle_receive_icmp_packet( const std::size_t &bytes_transferred );
     void print_echo_reply(
             const IcmpPacket &icmp_packet,
             const std::size_t &bytes_transferred
     ) const;
+    void print_destination_unreachable(
+            const IcmpPacket &icmp_packet
+    ) const;
 
     void set_ping_status( BoostPinger::PingStatus ping_status );
 
@@ -63,7 +67,7 @@ private:
     boost::asio::io_service &IoService;
     boost::asio::ip::icmp::endpoint DestinationEndpoint;
     boost::asio::ip::icmp::socket Socket;
-    boost::asio::deadline_timer Timer;
+    boost::asio::deadline_timer IcmpPacketReceiveTimer;
     uint16_t SequenceNumber;
     boost::posix_time::ptime TimeSent;
     boost::asio::streambuf ReplyBuffer;