Created an ICMP packet class abstraction and modularizing the BoostPinger
authorGuilherme Maciel Ferreira <guilherme.maciel.ferreira@intra2net.com>
Mon, 28 Feb 2011 13:59:17 +0000 (14:59 +0100)
committerGuilherme Maciel Ferreira <guilherme.maciel.ferreira@intra2net.com>
Mon, 28 Feb 2011 15:28:35 +0000 (16:28 +0100)
- Hide the ICMP packet management from BoostPinger class
- Split functionality in other methods (e.g send_echo_request() and print_echo_reply()

src/CMakeLists.txt
src/ping/boostpinger.cpp
src/ping/boostpinger.h
src/ping/icmpheader.cpp
src/ping/icmpheader.h
src/ping/icmppacket.cpp [new file with mode: 0644]
src/ping/icmppacket.h [new file with mode: 0644]
src/ping/ipv4header.cpp
src/ping/ipv4header.h

index c3f8d76..19c48e7 100644 (file)
@@ -12,6 +12,7 @@ set( SOURCES
     ping/boostpinger.cpp
     ping/host.cpp
     ping/icmpheader.cpp
+    ping/icmppacket.cpp
     ping/ipv4header.cpp
     ping/pingcheck.cpp
     ping/pinger.cpp
index 1adb386..75b7b92 100644 (file)
@@ -7,8 +7,8 @@
 #include <sys/socket.h>
 #include <sys/types.h>
 
-#include "icmpdata.h"
 #include "icmpchecksumcalculator.h"
+#include "icmpdata.h"
 #include "icmpheader.h"
 #include "ipv4header.h"
 #include "boostpinger.h"
@@ -53,26 +53,24 @@ void BoostPinger::ping( const std::string &destination )
 
 void BoostPinger::start_send()
 {
-    IcmpData data( "ping message" );
+    IcmpData icmp_data( "ping message" );
 
     // Create an ICMP header for an echo request.
     SequenceNumber++;
     IcmpHeader::IcmpType type = IcmpHeader::EchoRequest;
     uint8_t code = 0;
     uint16_t identifier = get_identifier();
-    IcmpChecksumCalculator calculator( data.begin(), data.end() );
+    IcmpChecksumCalculator calculator( icmp_data.begin(), icmp_data.end() );
     uint16_t checksum = calculator.compute( type, code, identifier, SequenceNumber );
-    IcmpHeader echo_request( type, code, checksum, identifier, SequenceNumber );
+    IcmpHeader icmp_header( type, code, checksum, identifier, SequenceNumber );
 
-    // Encode the request packet.
-    boost::asio::streambuf request_buffer;
-    std::ostream os( &request_buffer );
-    os << echo_request << data;
+    // Encode the ICMP header and data in an ICMP packet.
+    IcmpPacket icmp_echo_request_packet( icmp_header, icmp_data );
 
     int ping_times = SequenceNumber;
     if ( ping_times < 4 )
     {
-        send( request_buffer );
+        send_echo_request( icmp_echo_request_packet );
     }
     else
     {
@@ -80,8 +78,12 @@ void BoostPinger::start_send()
     }
 }
 
-void BoostPinger::send( const boost::asio::streambuf &request_buffer )
+void BoostPinger::send_echo_request( const IcmpPacket &icmp_packet )
 {
+    boost::asio::streambuf request_buffer;
+    std::ostream os( &request_buffer );
+    os << icmp_packet;
+
     // Send the request.
     TimeSent = posix_time::microsec_clock::universal_time();
     Socket.send_to( request_buffer.data(), DestinationEndpoint );
@@ -122,16 +124,13 @@ void BoostPinger::handle_receive( const std::size_t &length )
 
     // Decode the reply packet.
     std::istream is( &ReplyBuffer );
-    Ipv4Header ipv4_hdr;
-    IcmpHeader icmp_hdr;
-    is >> ipv4_hdr >> icmp_hdr;
+    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_hdr.get_type() == IcmpHeader::EchoReply
-            && icmp_hdr.get_identifier() == get_identifier()
-            && icmp_hdr.get_sequence_number() == SequenceNumber )
+    if ( is && icmp_packet.match( IcmpHeader::EchoReply, get_identifier(), SequenceNumber ) )
     {
         // If this is the first reply, interrupt the five second timeout.
         if ( NumReplies == 0 )
@@ -140,17 +139,28 @@ void BoostPinger::handle_receive( const std::size_t &length )
         ++NumReplies;
 
         // Print out some information about the reply packet.
-        posix_time::ptime now = posix_time::microsec_clock::universal_time();
-        std::cout << length - ipv4_hdr.get_header_length() << " bytes from "
-                << ipv4_hdr.get_source_address() << ": icmp_seq="
-                << icmp_hdr.get_sequence_number() << ", ttl="
-                << ipv4_hdr.get_time_to_live() << ", time="
-                << (now - TimeSent).total_milliseconds() << " ms" << std::endl;
+        print_echo_reply( icmp_packet, length );
     }
 
     start_receive();
 }
 
+void BoostPinger::print_echo_reply(
+        const IcmpPacket &icmp_packet,
+        const std::size_t &length
+)
+{
+    Ipv4Header ipv4_hdr = icmp_packet.get_ip_header();
+    IcmpHeader icmp_hdr = icmp_packet.get_icmp_header();
+
+    posix_time::ptime now = posix_time::microsec_clock::universal_time();
+    std::cout << ( length - ipv4_hdr.get_header_length() ) << " bytes from "
+            << ipv4_hdr.get_source_address() << ": icmp_seq="
+            << icmp_hdr.get_sequence_number() << ", ttl="
+            << ipv4_hdr.get_time_to_live() << ", time="
+            << (now - TimeSent).total_milliseconds() << " ms" << std::endl;
+}
+
 uint16_t BoostPinger::get_identifier()
 {
     return static_cast<uint16_t> ( ::getpid() );
index b4a1e40..1fdccfe 100644 (file)
@@ -3,6 +3,7 @@
 
 #include <boost/asio.hpp>
 
+#include "icmppacket.h"
 #include "pinger.h"
 
 using boost::asio::ip::address;
@@ -23,11 +24,15 @@ public:
 
 private:
     void start_send();
-    void send( const boost::asio::streambuf &request_buffer );
+    void send_echo_request( const IcmpPacket &icmp_packet );
     void handle_timeout();
 
     void start_receive();
     void handle_receive( const std::size_t &length );
+    void print_echo_reply(
+            const IcmpPacket &icmp_packet,
+            const std::size_t &length
+    );
 
     uint16_t get_identifier();
 
index d1ce175..9ca552c 100644 (file)
@@ -81,19 +81,25 @@ void IcmpHeader::set_sequence_number( uint16_t sequence_number )
     encode( 6, 7, sequence_number );
 }
 
-std::istream& operator>>( std::istream& is, IcmpHeader& header )
+std::istream& operator>>(
+        std::istream &is,
+        IcmpHeader &header
+)
 {
     return is.read( reinterpret_cast<char*> ( header.Rep ), 8 );
 }
 
-std::ostream& operator<<( std::ostream& os, const IcmpHeader& header )
+std::ostream& operator<<(
+        std::ostream &os,
+        const IcmpHeader &header
+)
 {
     return os.write( reinterpret_cast<const char*> ( header.Rep ), 8 );
 }
 
 uint16_t IcmpHeader::decode( int a, int b ) const
 {
-    return (Rep[ a ] << 8) + Rep[ b ];
+    return ( Rep[ a ] << 8 ) + Rep[ b ];
 }
 
 void IcmpHeader::encode( int a, int b, uint16_t n )
index 61e1b0e..ae354d3 100644 (file)
@@ -68,11 +68,13 @@ public:
     void set_sequence_number( const uint16_t sequence_number );
 
     friend std::istream& operator>>(
-            std::istream& is,
-            IcmpHeader& header );
+            std::istream &is,
+            IcmpHeader &header
+    );
     friend std::ostream& operator<<(
-            std::ostream& os,
-            const IcmpHeader& header );
+            std::ostream &os,
+            const IcmpHeader &header
+    );
 
 private:
     uint16_t decode( int a, int b ) const;
diff --git a/src/ping/icmppacket.cpp b/src/ping/icmppacket.cpp
new file mode 100644 (file)
index 0000000..a8ab37d
--- /dev/null
@@ -0,0 +1,74 @@
+#include "icmppacket.h"
+
+//-----------------------------------------------------------------------------
+// IcmpPacket
+//-----------------------------------------------------------------------------
+
+IcmpPacket::IcmpPacket() :
+    IpHeader(),
+    IcmpPayloadHeader(),
+    IcmpPayloadData()
+{
+}
+
+IcmpPacket::IcmpPacket(
+        const IcmpHeader &icmp_header,
+        const IcmpData &icmp_data
+) :
+    IpHeader(),
+    IcmpPayloadHeader( icmp_header ),
+    IcmpPayloadData( icmp_data )
+{
+}
+
+IcmpPacket::~IcmpPacket()
+{
+}
+
+Ipv4Header IcmpPacket::get_ip_header() const
+{
+    return IpHeader;
+}
+
+IcmpHeader IcmpPacket::get_icmp_header() const
+{
+    return IcmpPayloadHeader;
+}
+
+IcmpData IcmpPacket::get_icmp_data() const
+{
+    return IcmpPayloadData;
+}
+
+bool IcmpPacket::match(
+        IcmpHeader::IcmpType type,
+        uint16_t identifier,
+        uint16_t sequence_number
+)
+{
+    bool type_match = IcmpPayloadHeader.get_type() == type;
+    bool identifier_match = IcmpPayloadHeader.get_identifier() == identifier;
+    bool seq_num_match = IcmpPayloadHeader.get_sequence_number() == sequence_number;
+
+    return ( type_match && identifier_match && seq_num_match );
+}
+
+std::istream& operator>>(
+        std::istream &is,
+        IcmpPacket &packet
+)
+{
+    is >> packet.IpHeader >> packet.IcmpPayloadHeader >> packet.IcmpPayloadData;
+
+    return is;
+}
+
+std::ostream& operator<<(
+        std::ostream& os,
+        const IcmpPacket& packet
+)
+{
+    os << packet.IcmpPayloadHeader << packet.IcmpPayloadData;
+
+    return os;
+}
diff --git a/src/ping/icmppacket.h b/src/ping/icmppacket.h
new file mode 100644 (file)
index 0000000..4ef7803
--- /dev/null
@@ -0,0 +1,89 @@
+#ifndef ICMPPACKET_H
+#define ICMPPACKET_H
+
+#include <istream>
+#include <ostream>
+#include <stdint.h>
+
+#include "ipv4header.h"
+#include "icmpheader.h"
+#include "icmpdata.h"
+
+//-----------------------------------------------------------------------------
+// IcmpPacket
+//-----------------------------------------------------------------------------
+// ICMP packet/message format:
+//
+// 0               8               16                             31
+// +-------+-------+---------------+------------------------------+      ---
+// |       |       |               |                              |       ^
+// |version|header |    type of    |    total length in bytes     |       |
+// |  (4)  | length|    service    |                              |       |
+// +-------+-------+---------------+-+-+-+------------------------+       |
+// |                               | | | |                        |       |
+// |        identification         |0|D|M|    fragment offset     |       |
+// |                               | |F|F|                        |       |
+// +---------------+---------------+-+-+-+------------------------+       |
+// |               |               |                              |       |
+// | time to live  |   protocol    |       header checksum        |  IPv4 Header
+// |               |               |                              |   20 bytes
+// +---------------+---------------+------------------------------+       |
+// |                                                              |       |
+// |                      source IPv4 address                     |       |
+// |                                                              |       |
+// +--------------------------------------------------------------+       |
+// |                                                              |       |
+// |                   destination IPv4 address                   |       |
+// |                                                              |       v
+// +---------------+---------------+------------------------------+      ---
+// |               |               |                              |       ^
+// |     type      |     code      |          checksum            |       |
+// |               |               |                              |       |
+// +---------------+---------------+------------------------------+       |
+// |                               |                              | ICMP Payload
+// |          identifier           |       sequence number        | (header +
+// |                               |                              |       data)
+// +-------------------------------+------------------------------+   8+ bytes
+// |                                                              |       |
+// |                      data (optional)                         |       |
+// |                                                              |       v
+// +-------------------------------+------------------------------+      ---
+//
+//-----------------------------------------------------------------------------
+
+class IcmpPacket
+{
+public:
+    IcmpPacket();
+    IcmpPacket(
+            const IcmpHeader &icmp_header,
+            const IcmpData &icmp_data
+    );
+    virtual ~IcmpPacket();
+
+    Ipv4Header get_ip_header() const;
+    IcmpHeader get_icmp_header() const;
+    IcmpData get_icmp_data() const;
+
+    bool match(
+            IcmpHeader::IcmpType type,
+            uint16_t identifier,
+            uint16_t sequence_number
+    );
+
+    friend std::istream& operator>>(
+            std::istream &is,
+            IcmpPacket &packet
+    );
+    friend std::ostream& operator<<(
+            std::ostream &os,
+            const IcmpPacket &packet
+    );
+
+private:
+    Ipv4Header IpHeader;
+    IcmpHeader IcmpPayloadHeader;
+    IcmpData IcmpPayloadData;
+};
+
+#endif /* ICMPPACKET_H */
index 028b717..ed5ab10 100644 (file)
@@ -84,7 +84,10 @@ boost::asio::ip::address_v4 Ipv4Header::get_destination_address() const
     return boost::asio::ip::address_v4( bytes );
 }
 
-std::istream& operator>>( std::istream& is, Ipv4Header& header )
+std::istream& operator>>(
+        std::istream &is,
+        Ipv4Header &header
+)
 {
     is.read( reinterpret_cast<char*> ( header.Rep ), 20 );
     if ( header.get_version() != 4 )
index 638a08a..f03e751 100644 (file)
@@ -63,7 +63,10 @@ public:
     boost::asio::ip::address_v4 get_source_address() const;
     boost::asio::ip::address_v4 get_destination_address() const;
 
-    friend std::istream& operator>>( std::istream& is, Ipv4Header& header );
+    friend std::istream& operator>>(
+            std::istream &is,
+            Ipv4Header &header
+    );
 
 private:
     uint16_t decode( int a, int b ) const;