--- /dev/null
+# cmake out-of-source build directory
+build
+
+# eclipse IDE project files and directories
+Debug
+.cproject
+.project
+.directory
--- /dev/null
+cmake_minimum_required( VERSION 2.6 )
+
+project( pinger CXX )
+
+# set the directory where the executable will be placed
+set( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} )
+
+# set the directory where the make install places the executable
+#set( CMAKE_INSTALL_PREFIX ${CMAKE_CURRENT_BINARY_DIR} )
+
+add_subdirectory( src )
--- /dev/null
+set( SOURCES
+ icmp_header.cpp
+ ipv4_header.cpp
+ pinger.cpp
+ main.cpp
+)
+
+set( TARGET pinger )
+
+set( Boost_USE_STATIC_LIBS ON )
+set( Boost_USE_MULTITHREADED ON )
+set( Boost_USE_STATIC_RUNTIME OFF )
+find_package( Boost 1.36.0 REQUIRED COMPONENTS filesystem )
+
+include_directories( ${Boost_INCLUDE_DIRS} )
+add_executable( ${TARGET} ${SOURCES} )
+target_link_libraries( ${TARGET} ${Boost_LIBRARIES} )
+
+install(
+ TARGETS ${TARGET}
+ DESTINATION .
+)
\ No newline at end of file
--- /dev/null
+#include "icmp_header.hpp"
+
+IcmpHeader::IcmpHeader()
+{
+ std::fill( m_rep, m_rep + sizeof(m_rep), 0 );
+}
+
+unsigned char IcmpHeader::type() const
+{
+ return m_rep[ 0 ];
+}
+
+unsigned char IcmpHeader::code() const
+{
+ return m_rep[ 1 ];
+}
+
+unsigned short IcmpHeader::checksum() const
+{
+ return decode( 2, 3 );
+}
+
+unsigned short IcmpHeader::identifier() const
+{
+ return decode( 4, 5 );
+}
+
+unsigned short IcmpHeader::sequence_number() const
+{
+ return decode( 6, 7 );
+}
+
+void IcmpHeader::type( unsigned char n )
+{
+ m_rep[ 0 ] = n;
+}
+
+void IcmpHeader::code( unsigned char n )
+{
+ m_rep[ 1 ] = n;
+}
+
+void IcmpHeader::checksum( unsigned short n )
+{
+ encode( 2, 3, n );
+}
+
+void IcmpHeader::identifier( unsigned short n )
+{
+ encode( 4, 5, n );
+}
+
+void IcmpHeader::sequence_number( unsigned short n )
+{
+ encode( 6, 7, n );
+}
+
+std::istream& operator>>( std::istream& is, IcmpHeader& header )
+{
+ return is.read( reinterpret_cast<char*> ( header.m_rep ), 8 );
+}
+
+std::ostream& operator<<( std::ostream& os, const IcmpHeader& header )
+{
+ return os.write( reinterpret_cast<const char*> ( header.m_rep ), 8 );
+}
+
+unsigned short IcmpHeader::decode( int a, int b ) const
+{
+ return (m_rep[ a ] << 8) + m_rep[ b ];
+}
+
+void IcmpHeader::encode( int a, int b, unsigned short n )
+{
+ m_rep[ a ] = static_cast<unsigned char> ( n >> 8 );
+ m_rep[ b ] = static_cast<unsigned char> ( n & 0xFF );
+}
--- /dev/null
+#ifndef ICMP_HEADER_HPP
+#define ICMP_HEADER_HPP
+
+#include <istream>
+#include <ostream>
+#include <algorithm>
+
+// ICMP header format:
+//
+// 0 8 16 31
+// +---------------+---------------+------------------------------+ ---
+// | | | | ^
+// | type | code | checksum | |
+// | | | | |
+// +---------------+---------------+------------------------------+ 8 bytes
+// | | | |
+// | identifier | sequence number | |
+// | | | v
+// +-------------------------------+------------------------------+ ---
+
+class IcmpHeader
+{
+public:
+ enum IcmpType
+ {
+ EchoReply = 0,
+ DestinationUnreachable = 3,
+ SourceQuench = 4,
+ Redirect = 5,
+ EchoRequest = 8,
+ TimeExceeded = 11,
+ ParameterProblem = 12,
+ TimestampRequest = 13,
+ TimestampReply = 14,
+ InfoRequest = 15,
+ InfoReply = 16,
+ AddressRequest = 17,
+ AddressReply = 18
+ };
+
+ IcmpHeader();
+
+ unsigned char type() const;
+ unsigned char code() const;
+ unsigned short checksum() const;
+ unsigned short identifier() const;
+ unsigned short sequence_number() const;
+
+ void type( const unsigned char n );
+ void code( const unsigned char n );
+ void checksum( const unsigned short n );
+ void identifier( const unsigned short n );
+ void sequence_number( const unsigned short n );
+
+ friend std::istream& operator>>(
+ std::istream& is,
+ IcmpHeader& header );
+ friend std::ostream& operator<<(
+ std::ostream& os,
+ const IcmpHeader& header );
+
+private:
+ unsigned short decode( int a, int b ) const;
+ void encode( int a, int b, unsigned short n );
+
+ unsigned char m_rep[ 8 ];
+};
+
+template<typename Iterator>
+ void compute_checksum(
+ IcmpHeader& header,
+ Iterator body_begin,
+ Iterator body_end )
+ {
+ unsigned int sum = (header.type() << 8) + header.code()
+ + header.identifier() + header.sequence_number();
+
+ Iterator body_iter = body_begin;
+ while ( body_iter != body_end )
+ {
+ sum += (static_cast<unsigned char> ( *body_iter++ ) << 8);
+ if (body_iter != body_end)
+ sum += static_cast<unsigned char> ( *body_iter++ );
+ }
+
+ sum = (sum >> 16) + (sum & 0xFFFF);
+ sum += (sum >> 16);
+ header.checksum( static_cast<unsigned short> ( ~sum ) );
+ }
+
+#endif // ICMP_HEADER_HPP
--- /dev/null
+#include "ipv4_header.hpp"
+
+Ipv4Header::Ipv4Header()
+{
+ std::fill( m_rep, m_rep + sizeof(m_rep), 0 );
+}
+
+unsigned char Ipv4Header::version() const
+{
+ return (m_rep[ 0 ] >> 4) & 0xF;
+}
+
+unsigned short Ipv4Header::header_length() const
+{
+ return (m_rep[ 0 ] & 0xF) * 4;
+}
+
+unsigned char Ipv4Header::type_of_service() const
+{
+ return m_rep[ 1 ];
+}
+
+unsigned short Ipv4Header::total_length() const
+{
+ return decode( 2, 3 );
+}
+
+unsigned short Ipv4Header::identification() const
+{
+ return decode( 4, 5 );
+}
+
+bool Ipv4Header::dont_fragment() const
+{
+ return (m_rep[ 6 ] & 0x40) != 0;
+}
+
+bool Ipv4Header::more_fragments() const
+{
+ return (m_rep[ 6 ] & 0x20) != 0;
+}
+
+unsigned short Ipv4Header::fragment_offset() const
+{
+ return decode( 6, 7 ) & 0x1FFF;
+}
+
+unsigned int Ipv4Header::time_to_live() const
+{
+ return m_rep[ 8 ];
+}
+
+unsigned char Ipv4Header::protocol() const
+{
+ return m_rep[ 9 ];
+}
+
+unsigned short Ipv4Header::header_checksum() const
+{
+ return decode( 10, 11 );
+}
+
+boost::asio::ip::address_v4 Ipv4Header::source_address() const
+{
+ boost::asio::ip::address_v4::bytes_type bytes = { {
+ m_rep[ 12 ],
+ m_rep[ 13 ],
+ m_rep[ 14 ],
+ m_rep[ 15 ] } };
+ return boost::asio::ip::address_v4( bytes );
+}
+
+boost::asio::ip::address_v4 Ipv4Header::destination_address() const
+{
+ boost::asio::ip::address_v4::bytes_type bytes = { {
+ m_rep[ 16 ],
+ m_rep[ 17 ],
+ m_rep[ 18 ],
+ m_rep[ 19 ] } };
+ return boost::asio::ip::address_v4( bytes );
+}
+
+std::istream& operator>>( std::istream& is, Ipv4Header& header )
+{
+ is.read( reinterpret_cast<char*> ( header.m_rep ), 20 );
+ if (header.version() != 4)
+ is.setstate( std::ios::failbit );
+ std::streamsize options_length = header.header_length() - 20;
+ if (options_length < 0 || options_length > 40)
+ is.setstate( std::ios::failbit );
+ else
+ is.read( reinterpret_cast<char*> ( header.m_rep ) + 20, options_length );
+ return is;
+}
+
+unsigned short Ipv4Header::decode( int a, int b ) const
+{
+ return (m_rep[ a ] << 8) + m_rep[ b ];
+}
--- /dev/null
+#ifndef IPV4_HEADER_HPP
+#define IPV4_HEADER_HPP
+
+#include <algorithm>
+#include <boost/asio/ip/address_v4.hpp>
+
+// IPv4 header 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 | 20 bytes
+// | | | | |
+// +---------------+---------------+------------------------------+ |
+// | | |
+// | source IPv4 address | |
+// | | |
+// +--------------------------------------------------------------+ |
+// | | |
+// | destination IPv4 address | |
+// | | v
+// +--------------------------------------------------------------+ ---
+// | | ^
+// | | |
+// / options (if any) / 0 - 40
+// / / bytes
+// | | |
+// | | v
+// +--------------------------------------------------------------+ ---
+
+class Ipv4Header
+{
+public:
+ Ipv4Header();
+
+ unsigned char version() const;
+ unsigned short header_length() const;
+ unsigned char type_of_service() const;
+ unsigned short total_length() const;
+ unsigned short identification() const;
+
+ bool dont_fragment() const;
+ bool more_fragments() const;
+ unsigned short fragment_offset() const;
+ unsigned int time_to_live() const;
+ unsigned char protocol() const;
+ unsigned short header_checksum() const;
+
+ boost::asio::ip::address_v4 source_address() const;
+ boost::asio::ip::address_v4 destination_address() const;
+
+ friend std::istream& operator>>( std::istream& is, Ipv4Header& header );
+
+private:
+ unsigned short decode( int a, int b ) const;
+
+ unsigned char m_rep[ 60 ];
+};
+
+#endif // IPV4_HEADER_HPP
--- /dev/null
+#include <iostream>
+
+#include "pinger.hpp"
+
+int main( int argc, char* argv[] )
+{
+ try
+ {
+ if (argc != 2)
+ {
+ std::cerr << "Usage: ping <host>" << std::endl;
+ std::cerr << "(You may need to run this program as root.)"
+ << std::endl;
+ return 1;
+ }
+
+ boost::asio::io_service io_service;
+ Pinger p( io_service, argv[ 1 ] );
+ io_service.run();
+ }
+ catch (std::exception& e)
+ {
+ std::cerr << "Exception: " << e.what() << std::endl;
+ }
+}
--- /dev/null
+#include <boost/bind.hpp>
+#include <istream>
+#include <iostream>
+#include <ostream>
+
+#include <net/if.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include "icmp_header.hpp"
+#include "ipv4_header.hpp"
+#include "pinger.hpp"
+
+//-----------------------------------------------------------------------------
+// Pinger
+//-----------------------------------------------------------------------------
+
+Pinger::Pinger( boost::asio::io_service& io_service, const char* destination ) :
+ m_resolver( io_service ), m_timer( io_service ), m_sequence_number( 0 ),
+ m_num_replies( 0 ), m_socket( io_service, icmp::v4() )
+{
+ try
+ {
+ std::string ip = "172.16.1.149";
+ uint32_t port = 21;
+ icmp::endpoint ep( address::from_string( ip ), port );
+ m_socket.bind( ep );
+ }
+ catch (boost::system::system_error &e)
+ {
+ std::cerr << "exception" << std::endl;
+ }
+
+ icmp::resolver::query query( icmp::v4(), destination, "" );
+ m_destination = *m_resolver.resolve( query );
+
+ start_send();
+ start_receive();
+}
+
+Pinger::~Pinger()
+{
+}
+
+void Pinger::start_send()
+{
+ std::string body( "ping message" );
+
+ // Create an ICMP header for an echo request.
+ IcmpHeader echo_request;
+ echo_request.type( IcmpHeader::EchoRequest );
+ echo_request.code( 0 );
+ echo_request.identifier( get_identifier() );
+ m_sequence_number++;
+ echo_request.sequence_number( m_sequence_number );
+ compute_checksum( echo_request, body.begin(), body.end() );
+
+ // Encode the request packet.
+ boost::asio::streambuf request_buffer;
+ std::ostream os( &request_buffer );
+ os << echo_request << body;
+
+ // Send the request.
+ m_time_sent = posix_time::microsec_clock::universal_time();
+ m_socket.send_to( request_buffer.data(), m_destination );
+
+ // Wait up to five seconds for a reply.
+ m_num_replies = 0;
+ m_timer.expires_at( m_time_sent + posix_time::seconds( 5 ) );
+ m_timer.async_wait( boost::bind( &Pinger::handle_timeout, this ) );
+}
+
+void Pinger::handle_timeout()
+{
+ if (m_num_replies == 0)
+ std::cout << "Request timed out" << std::endl;
+
+ // Requests must be sent no less than one second apart.
+ m_timer.expires_at( m_time_sent + posix_time::seconds( 1 ) );
+ m_timer.async_wait( boost::bind( &Pinger::start_send, this ) );
+}
+
+void Pinger::start_receive()
+{
+ // Discard any data already in the buffer.
+ m_reply_buffer.consume( m_reply_buffer.size() );
+
+ // Wait for a reply. We prepare the buffer to receive up to 64KB.
+ m_socket.async_receive(
+ m_reply_buffer.prepare( 65536 ),
+ boost::bind( &Pinger::handle_receive, this, _2 ) );
+}
+
+void Pinger::handle_receive( std::size_t length )
+{
+ // The actual number of bytes received is committed to the buffer so that we
+ // can extract it using a std::istream object.
+ m_reply_buffer.commit( length );
+
+ // Decode the reply packet.
+ std::istream is( &m_reply_buffer );
+ Ipv4Header ipv4_hdr;
+ IcmpHeader icmp_hdr;
+ is >> ipv4_hdr >> icmp_hdr;
+
+ // 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.type() == IcmpHeader::EchoReply
+ && icmp_hdr.identifier() == get_identifier()
+ && icmp_hdr.sequence_number() == m_sequence_number)
+ {
+ // If this is the first reply, interrupt the five second timeout.
+ if (m_num_replies++ == 0)
+ m_timer.cancel();
+
+ // Print out some information about the reply packet.
+ posix_time::ptime now = posix_time::microsec_clock::universal_time();
+ std::cout << length - ipv4_hdr.header_length() << " bytes from "
+ << ipv4_hdr.source_address() << ": icmp_seq="
+ << icmp_hdr.sequence_number() << ", ttl="
+ << ipv4_hdr.time_to_live() << ", time="
+ << (now - m_time_sent).total_milliseconds() << " ms"
+ << std::endl;
+ }
+
+ start_receive();
+}
+
+uint16_t Pinger::get_identifier()
+{
+ return static_cast<uint16_t> ( ::getpid() );
+}
--- /dev/null
+#ifndef PINGER_HPP
+#define PINGER_HPP
+
+#include <boost/asio.hpp>
+
+using boost::asio::ip::address;
+using boost::asio::ip::icmp;
+using boost::asio::deadline_timer;
+
+namespace posix_time = boost::posix_time;
+
+//-----------------------------------------------------------------------------
+// Pinger
+//-----------------------------------------------------------------------------
+
+class Pinger
+{
+public:
+ Pinger( boost::asio::io_service& io_service, const char* destination );
+ virtual ~Pinger();
+
+private:
+ void start_send();
+
+ void handle_timeout();
+ void start_receive();
+ void handle_receive( std::size_t length );
+
+ static uint16_t get_identifier();
+
+ icmp::resolver m_resolver;
+ icmp::endpoint m_destination;
+ icmp::socket m_socket;
+ deadline_timer m_timer;
+ uint16_t m_sequence_number;
+ posix_time::ptime m_time_sent;
+ boost::asio::streambuf m_reply_buffer;
+ std::size_t m_num_replies;
+};
+
+#endif /* PINGER_HPP */