From: Christian Herdtweck Date: Wed, 18 Mar 2015 10:46:13 +0000 (+0100) Subject: added code to parse ICMP type TimeExceeded X-Git-Url: http://developer.intra2net.com/git/?a=commitdiff_plain;h=15023b9956eab6cfb37b22a14a8c165622c5384f;p=pingcheck added code to parse ICMP type TimeExceeded * format is identical to DestinationUnreachable and other ICMP replies --> created a common half-abstract base class IcmpData_PingFailReply * added a match_time_exceeded to IcmpPacket which is called by IcmpPinger --- diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 14801e0..9bf618b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -77,8 +77,10 @@ set(SOURCES host/pingrotate.cpp host/pingscheduler.cpp icmp/icmpdata.cpp + icmp/icmpdata_pingfailreply.cpp icmp/icmpechodata.cpp icmp/icmpdestinationunreachabledata.cpp + icmp/icmptimeexceededdata.cpp icmp/icmpheader.cpp icmp/icmppacket.cpp icmp/icmppinger.cpp diff --git a/src/icmp/icmpdata.cpp b/src/icmp/icmpdata.cpp index 3db5912..a6020e0 100644 --- a/src/icmp/icmpdata.cpp +++ b/src/icmp/icmpdata.cpp @@ -44,6 +44,10 @@ bool IcmpData::match_destination_unreachable(const uint16_t identifier, const uint16_t sequence_number) const { return false; } +bool IcmpData::match_time_exceeded(const uint16_t identifier, + const uint16_t sequence_number) const +{ return false; } + inline std::size_t IcmpData::get_size() { return size; } diff --git a/src/icmp/icmpdata.h b/src/icmp/icmpdata.h index f53f160..042aa32 100644 --- a/src/icmp/icmpdata.h +++ b/src/icmp/icmpdata.h @@ -52,6 +52,9 @@ public: virtual bool match_destination_unreachable(const uint16_t identifier, const uint16_t sequence_number) const; + virtual bool match_time_exceeded(const uint16_t identifier, + const uint16_t sequence_number) const; + inline std::size_t get_size(); virtual std::istream& read( std::istream &is); diff --git a/src/icmp/icmpdata_pingfailreply.cpp b/src/icmp/icmpdata_pingfailreply.cpp new file mode 100644 index 0000000..967f273 --- /dev/null +++ b/src/icmp/icmpdata_pingfailreply.cpp @@ -0,0 +1,80 @@ +/* + The software in this package is distributed under the GNU General + Public License version 2 (with a special exception described below). + + A copy of GNU General Public License (GPL) is included in this distribution, + in the file COPYING.GPL. + + As a special exception, if other files instantiate templates or use macros + or inline functions from this file, or you compile this file and link it + with other works to produce a work based on this file, this file + does not by itself cause the resulting work to be covered + by the GNU General Public License. + + However the source code for this file must still be made available + in accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work based + on this file might be covered by the GNU General Public License. + + Christian Herdtweck, Intra2net AG 2015 + Based on an example in Boost Documentation (by Christopher M. Kohlhoff) + and adaptation by Guilherme M. Ferreira + */ + +#include "icmp/icmpdata_pingfailreply.h" + + +IcmpData_PingFailReply::IcmpData_PingFailReply(const std::size_t size_arg) + : IcmpData( size_arg ) +{} + +bool IcmpData_PingFailReply::match_echo_reply( + const uint16_t identifier, + const uint16_t sequence_number) const +{ return false; } + +bool IcmpData_PingFailReply::match_ping_request( + const uint16_t identifier, + const uint16_t sequence_number) const +{ + return identifier == get_icmp_identifier() && + sequence_number == get_icmp_sequence_number(); +} + + +uint16_t IcmpData_PingFailReply::get_icmp_identifier() const +{ return get_icmp_request_data(5); } + +uint16_t IcmpData_PingFailReply::get_icmp_sequence_number() const +{ return get_icmp_request_data(7); } + +uint16_t IcmpData_PingFailReply::get_icmp_request_data( + const int data_offset) const +{ + // payload should be the original query, which is an IP packet. + // first check wheter that IP packet contains an ICMP message at all + bool is_icmp; + int offset = 4; // the 4 uninteresting bytes we need to skip + if (IcmpData::raw_data[offset+0] == 4) // IPv4 + { + is_icmp = IcmpData::raw_data[offset+9] == 1; + offset += 20; // 20 byte for IPv4 header + } + else if (IcmpData::raw_data[offset+0] == 6) // IPv6 + { + is_icmp = IcmpData::raw_data[offset+6] == 58; + offset += 40; // 40 byte for IPv6 header + } + + if ( !is_icmp ) + return static_cast(-1); + else + // if it is an icmp message, then the icmp packet comes right after + // the IP header. Inside the icmp packet we need bytes 5-8 + return IcmpData::raw_data.decode16(offset+data_offset, + offset+data_offset+1); +} + +// (created using vim -- the world's best text editor) + diff --git a/src/icmp/icmpdata_pingfailreply.h b/src/icmp/icmpdata_pingfailreply.h new file mode 100644 index 0000000..f47cc79 --- /dev/null +++ b/src/icmp/icmpdata_pingfailreply.h @@ -0,0 +1,89 @@ +/* + The software in this package is distributed under the GNU General + Public License version 2 (with a special exception described below). + + A copy of GNU General Public License (GPL) is included in this distribution, + in the file COPYING.GPL. + + As a special exception, if other files instantiate templates or use macros + or inline functions from this file, or you compile this file and link it + with other works to produce a work based on this file, this file + does not by itself cause the resulting work to be covered + by the GNU General Public License. + + However the source code for this file must still be made available + in accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work based + on this file might be covered by the GNU General Public License. + + Christian Herdtweck, Intra2net AG 2015 + Based on an example in Boost Documentation (by Christopher M. Kohlhoff) + and adaptation by Guilherme M. Ferreira + */ + + +#ifndef ICMP_DATA_PING_FAIL_REPLY_H +#define ICMP_DATA_PING_FAIL_REPLY_H + +#include +#include "icmp/icmpdata.h" + +/** + * structure of DestinationUnreachable or TimeExceeded or Source Quench packets + * if originated from an ICMP request: + * +---------------------------+ + * | reply IP header | 20/40 bytes --> not contained in here + * +---------------------------+ + * | reply ICMP header: | + * | type | code | checksum | 4 byte --> not contained in here ! + * | 4 uninteresting bytes | 4 byte --> contained in here! + * +---------------------------+ + * | request | 20 / 40 bytes + * | IP header | for IP v4 / v6 + * +---------------------------+ + * | request ICMP packet: | + * | type | code | checksum | 1+1+2 byte + * | identifier | seq. number | 2+2 byte + * | data | 4 byte + * | data | 4 byte + * +---------------------------+ + * (where "in here" means in variable raw_data of class + * IcmpData_PingFailReply) + * + * see also: https://tools.ietf.org/html/rfc792 (ICMP v4) + * https://tools.ietf.org/html/rfc4443 (ICMP v6) + * http://en.wikipedia.org/wiki/Internet_Control_Message_Protocol + * http://en.wikipedia.org/wiki/ICMPv6 + */ + +/** + * contains the request IP and ICMP header and possibly more data from request + */ +class IcmpData_PingFailReply : public IcmpData +{ +public: + IcmpData_PingFailReply(const std::size_t size_arg); + + bool match_echo_reply(const uint16_t identifier, + const uint16_t sequence_number) const; + + // not implementing match_destination_unreachable nor match_time_exceeded + + uint16_t get_icmp_identifier() const; + + uint16_t get_icmp_sequence_number() const; + + // not implementing print nor to_string + +protected: + uint16_t get_icmp_request_data(const int data_offset) const; + bool match_ping_request(const uint16_t identifier, + const uint16_t sequence_number) const; + +}; + +#endif + +// (created using vim -- the world's best text editor) + diff --git a/src/icmp/icmpdestinationunreachabledata.cpp b/src/icmp/icmpdestinationunreachabledata.cpp index c42d692..b3d7275 100644 --- a/src/icmp/icmpdestinationunreachabledata.cpp +++ b/src/icmp/icmpdestinationunreachabledata.cpp @@ -30,54 +30,21 @@ using I2n::Logger::GlobalLogger; IcmpDestinationUnreachableData::IcmpDestinationUnreachableData( const std::size_t size_arg) - : IcmpData( size_arg ) + : IcmpData_PingFailReply( size_arg ) {} -bool IcmpDestinationUnreachableData::match_echo_reply(const uint16_t identifier, - const uint16_t sequence_number) const -{ return false; } - bool IcmpDestinationUnreachableData::match_destination_unreachable( const uint16_t identifier, const uint16_t sequence_number) const { - return identifier == get_icmp_identifier() && - sequence_number == get_icmp_sequence_number(); + return IcmpData_PingFailReply::match_ping_request(identifier, + sequence_number); } - -uint16_t IcmpDestinationUnreachableData::get_icmp_identifier() const -{ return get_icmp_request_data(5); } - -uint16_t IcmpDestinationUnreachableData::get_icmp_sequence_number() const -{ return get_icmp_request_data(7); } - -uint16_t IcmpDestinationUnreachableData::get_icmp_request_data( - const int data_offset) const -{ - // payload should be the original query, which is an IP packet. - // first check wheter that IP packet contains an ICMP message at all - bool is_icmp; - int offset = 4; // the 4 uninteresting bytes we need to skip - if (IcmpData::raw_data[offset+0] == 4) // IPv4 - { - is_icmp = IcmpData::raw_data[offset+9] == 1; - offset += 20; // 20 byte for IPv4 header - } - else if (IcmpData::raw_data[offset+0] == 6) // IPv6 - { - is_icmp = IcmpData::raw_data[offset+6] == 58; - offset += 40; // 40 byte for IPv6 header - } - - if ( !is_icmp ) - return static_cast(-1); - else - // if it is an icmp message, then the icmp packet comes right after - // the IP header. Inside the icmp packet we need bytes 5-8 - return IcmpData::raw_data.decode16(offset+data_offset, - offset+data_offset+1); -} +bool IcmpDestinationUnreachableData::match_time_exceeded( + const uint16_t identifier, + const uint16_t sequence_number) const +{ return false; } /** * @brief Prints the destination unreachable messages. diff --git a/src/icmp/icmpdestinationunreachabledata.h b/src/icmp/icmpdestinationunreachabledata.h index 7910ec3..876ab67 100644 --- a/src/icmp/icmpdestinationunreachabledata.h +++ b/src/icmp/icmpdestinationunreachabledata.h @@ -28,50 +28,25 @@ #include #include -#include "icmp/icmpdata.h" +#include "icmp/icmpdata_pingfailreply.h" -/** - * structure of these packets (if originated from an ICMP request): - * +---------------------------+ - * | reply IP header | 20/40 bytes --> not contained in here - * +---------------------------+ - * | reply ICMP header: | - * | type | code | checksum | 4 byte --> not contained in here ! - * | 4 uninteresting bytes | 4 byte --> contained in here! - * +---------------------------+ - * | request | 20 / 40 bytes - * | IP header | for IP v4 / v6 - * +---------------------------+ - * | request ICMP packet: | - * | type | code | checksum | 1+1+2 byte - * | identifier | seq. number | 2+2 byte - * | data | 4 byte - * | data | 4 byte - * +---------------------------+ - * (where "in here" means in variable raw_data of class - * IcmpDestinationUnreachableData) - * - * see also: https://tools.ietf.org/html/rfc792 (ICMP v4) - * https://tools.ietf.org/html/rfc4443 (ICMP v6) - */ /** - * contains the request IP and ICMP header and possibly more data from request + * Data for a DestinationUnreachableMessage + * + * has option to interpret data as containing a Ping packet as reason for + * DestinationUnreachable reply */ -class IcmpDestinationUnreachableData : public IcmpData +class IcmpDestinationUnreachableData : public IcmpData_PingFailReply { public: IcmpDestinationUnreachableData(const std::size_t size_arg); - bool match_echo_reply(const uint16_t identifier, - const uint16_t sequence_number) const; - bool match_destination_unreachable(const uint16_t identifier, const uint16_t sequence_number) const; - uint16_t get_icmp_identifier() const; - - uint16_t get_icmp_sequence_number() const; + bool match_time_exceeded(const uint16_t identifier, + const uint16_t sequence_number) const; /** * @brief Prints the destination unreachable messages. @@ -85,9 +60,6 @@ public: std::string to_string() const; -protected: - uint16_t get_icmp_request_data(const int data_offset) const; - }; #endif diff --git a/src/icmp/icmpechodata.cpp b/src/icmp/icmpechodata.cpp index 9650774..f6c7ae3 100644 --- a/src/icmp/icmpechodata.cpp +++ b/src/icmp/icmpechodata.cpp @@ -54,6 +54,12 @@ bool IcmpEchoData::match_destination_unreachable( { return false; } +bool IcmpEchoData::match_time_exceeded( + const uint16_t identifier, + const uint16_t sequence_number) const +{ return false; } + + bool IcmpEchoData::match_echo_reply(const uint16_t identifier, const uint16_t sequence_number) const { diff --git a/src/icmp/icmpechodata.h b/src/icmp/icmpechodata.h index c6b865b..ac73adb 100644 --- a/src/icmp/icmpechodata.h +++ b/src/icmp/icmpechodata.h @@ -49,6 +49,9 @@ public: bool match_echo_reply(const uint16_t identifier, const uint16_t sequence_number) const; + bool match_time_exceeded(const uint16_t identifier, + const uint16_t sequence_number) const; + uint16_t get_identifier() const; uint16_t get_sequence_number() const; diff --git a/src/icmp/icmpheader.cpp b/src/icmp/icmpheader.cpp index bad7a6e..3a2047d 100644 --- a/src/icmp/icmpheader.cpp +++ b/src/icmp/icmpheader.cpp @@ -92,7 +92,8 @@ std::string IcmpHeader::to_string() const std::stringstream buf; buf << "[Icmp header type=" << static_cast(type) << ",code=" << static_cast(code) - << " (cksum:" << std::showbase << std::hex << checksum << ")]"; + // << " (cksum:" << std::showbase << std::hex << checksum << ")" + << "]"; return buf.str(); } diff --git a/src/icmp/icmppacket.cpp b/src/icmp/icmppacket.cpp index b90f481..b8021da 100644 --- a/src/icmp/icmppacket.cpp +++ b/src/icmp/icmppacket.cpp @@ -35,6 +35,7 @@ #include "ip/ipv6header.h" #include "icmp/icmpechodata.h" #include "icmp/icmpdestinationunreachabledata.h" +#include "icmp/icmptimeexceededdata.h" using I2n::Logger::GlobalLogger; @@ -101,6 +102,15 @@ bool IcmpPacket::match_destination_unreachable(const uint16_t identifier, sequence_number); } +bool IcmpPacket::match_time_exceeded(const uint16_t identifier, + const uint16_t sequence_number, + const address &destination_address) const +{ + return ip_head_ptr->get_source_address() == destination_address + && icmp_data_ptr->match_time_exceeded(identifier, + sequence_number); +} + bool IcmpPacket::match_echo_reply(const uint16_t identifier, const uint16_t sequence_number, const address &destination_address) const @@ -150,7 +160,7 @@ IcmpPacket::ReadReturnCode IcmpPacket::read( std::istream &is ) { GlobalLogger.error() << "IP version given to constructor (" << static_cast(ip_head_ptr->get_version()) << ") does not " - << "match the on in input stream (" << static_cast(version) + << "match the one in input stream (" << static_cast(version) << ")!" << std::endl; BOOST_ASSERT( !"Inconsistent IP version!" ); } @@ -186,6 +196,8 @@ IcmpPacket::ReadReturnCode IcmpPacket::read( std::istream &is ) else if ( !is.good() ) return IcmpPacket::ReadReturnCode_BAD_STREAM; + // could check validity of icmp type here + // calculate the size of the ICMP data std::streamsize data_length = static_cast( ip_head_ptr->get_total_length() ) @@ -212,6 +224,10 @@ IcmpPacket::ReadReturnCode IcmpPacket::read( std::istream &is ) || get_type_v6() == Icmpv6Type_DestinationUnreachable) icmp_data_ptr.reset( new IcmpDestinationUnreachableData( static_cast(data_length)) ); + else if (get_type_v4() == Icmpv4Type_TimeExceeded + || get_type_v6() == Icmpv6Type_TimeExceeded) + icmp_data_ptr.reset( new IcmpTimeExceededData( + static_cast(data_length)) ); else { // unspecified icmp data type, will consume right amount of data // from stream but return false for match_echo_request and diff --git a/src/icmp/icmppacket.h b/src/icmp/icmppacket.h index 85cec2e..4a4253a 100644 --- a/src/icmp/icmppacket.h +++ b/src/icmp/icmppacket.h @@ -84,6 +84,9 @@ public: const uint16_t sequence_number, const address &destination_address) const; + bool match_time_exceeded(const uint16_t identifier, + const uint16_t sequence_number, + const address &destination_address) const; /** * @brief print echo reply / destination unreachable message depending on * ICMP data type @@ -94,7 +97,7 @@ public: * @return void */ void print( const size_t &bytes_transferred, - const boost::posix_time::ptime &time_packet_sent ) const; + const boost::posix_time::ptime &time_packet_sent ) const; ReadReturnCode read(std::istream &is); diff --git a/src/icmp/icmppinger.cpp b/src/icmp/icmppinger.cpp index 5837062..67f3b34 100644 --- a/src/icmp/icmppinger.cpp +++ b/src/icmp/icmppinger.cpp @@ -344,6 +344,23 @@ bool IcmpPinger::handle_receive_icmp_packet(const IcmpPacketItem icmp_packet, IcmpPacketReceiveTimer.cancel(); //lint !e534 } + else if ( icmp_packet->match_time_exceeded( + Identifier, SequenceNumber, + DestinationEndpoint.address() ) ) + { + GlobalLogger.info() + << DestinationEndpoint.address().to_string() + << ": Received time exceeded" << endl; + + ReplyReceived = true; + does_match = true; + + icmp_packet->print( bytes_transferred, TimeSent ); + + set_ping_status( PingStatus_FailureDestinationUnreachable ); + + IcmpPacketReceiveTimer.cancel(); //lint !e534 + } else { GlobalLogger.info() diff --git a/src/icmp/icmptimeexceededdata.cpp b/src/icmp/icmptimeexceededdata.cpp new file mode 100644 index 0000000..5adf940 --- /dev/null +++ b/src/icmp/icmptimeexceededdata.cpp @@ -0,0 +1,67 @@ +/* + The software in this package is distributed under the GNU General + Public License version 2 (with a special exception described below). + + A copy of GNU General Public License (GPL) is included in this distribution, + in the file COPYING.GPL. + + As a special exception, if other files instantiate templates or use macros + or inline functions from this file, or you compile this file and link it + with other works to produce a work based on this file, this file + does not by itself cause the resulting work to be covered + by the GNU General Public License. + + However the source code for this file must still be made available + in accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work based + on this file might be covered by the GNU General Public License. + + Christian Herdtweck, Intra2net AG 2015 + Based on an example in Boost Documentation (by Christopher M. Kohlhoff) + and adaptation by Guilherme M. Ferreira + */ + +#include "icmp/icmptimeexceededdata.h" + +#include +using I2n::Logger::GlobalLogger; + + +IcmpTimeExceededData::IcmpTimeExceededData(const std::size_t size_arg) + : IcmpData_PingFailReply( size_arg ) +{} + +bool IcmpTimeExceededData::match_time_exceeded( + const uint16_t identifier, + const uint16_t sequence_number) const +{ + return IcmpData_PingFailReply::match_ping_request(identifier, + sequence_number); +} + +bool IcmpTimeExceededData::match_destination_unreachable( + const uint16_t identifier, + const uint16_t sequence_number) const +{ return false; } + +void IcmpTimeExceededData::print( + const size_t &bytes_received, + const boost::posix_time::ptime &time_packet_sent, + const std::string &remote_address, + const uint32_t ttl) const +{ + uint16_t sequence_number = get_icmp_sequence_number(); + + GlobalLogger.info() << "Time exceeded pinging " << remote_address + << " (icmp_seq=" << sequence_number << ")!" + << std::endl; +} + +std::string IcmpTimeExceededData::to_string() const +{ + return "[TimeExceededData]"; +} + +// (created using vim -- the world's best text editor) + diff --git a/src/icmp/icmptimeexceededdata.h b/src/icmp/icmptimeexceededdata.h new file mode 100644 index 0000000..5f2fc1a --- /dev/null +++ b/src/icmp/icmptimeexceededdata.h @@ -0,0 +1,63 @@ +/* + The software in this package is distributed under the GNU General + Public License version 2 (with a special exception described below). + + A copy of GNU General Public License (GPL) is included in this distribution, + in the file COPYING.GPL. + + As a special exception, if other files instantiate templates or use macros + or inline functions from this file, or you compile this file and link it + with other works to produce a work based on this file, this file + does not by itself cause the resulting work to be covered + by the GNU General Public License. + + However the source code for this file must still be made available + in accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work based + on this file might be covered by the GNU General Public License. + + Christian Herdtweck, Intra2net AG 2015 + Based on an example in Boost Documentation (by Christopher M. Kohlhoff) + and adaptation by Guilherme M. Ferreira + */ + + +#ifndef ICMP_TIME_EXCEEDED_DATA_H +#define ICMP_TIME_EXCEEDED_DATA_H + +#include +#include +#include "icmp/icmpdata_pingfailreply.h" + + +/** + * Data for a DestinationUnreachableMessage + * + * has option to interpret data as containing a Ping packet as reason for + * DestinationUnreachable reply + */ +class IcmpTimeExceededData : public IcmpData_PingFailReply +{ +public: + IcmpTimeExceededData(const std::size_t size_arg); + + bool match_destination_unreachable(const uint16_t identifier, + const uint16_t sequence_number) const; + + bool match_time_exceeded(const uint16_t identifier, + const uint16_t sequence_number) const; + + void print( const size_t &bytes_received, + const boost::posix_time::ptime &time_packet_sent, + const std::string &remote_address, + const uint32_t ttl) const; + + std::string to_string() const; + +}; + +#endif + +// (created using vim -- the world's best text editor) + diff --git a/test/CMakeLists.test_icmpv4header.txt b/test/CMakeLists.test_icmpv4header.txt index b2697dd..1133e30 100644 --- a/test/CMakeLists.test_icmpv4header.txt +++ b/test/CMakeLists.test_icmpv4header.txt @@ -3,6 +3,7 @@ add_executable(test_icmpv4header test_icmpv4header.cpp ${CMAKE_SOURCE_DIR}/src/boost_assert_handler.cpp ${CMAKE_SOURCE_DIR}/src/icmp/icmpdata.cpp + ${CMAKE_SOURCE_DIR}/src/icmp/icmpdata_pingfailreply.cpp ${CMAKE_SOURCE_DIR}/src/icmp/icmpechodata.cpp ${CMAKE_SOURCE_DIR}/src/icmp/icmpdestinationunreachabledata.cpp ${CMAKE_SOURCE_DIR}/src/icmp/icmpheader.cpp diff --git a/test/CMakeLists.test_icmpv6header.txt b/test/CMakeLists.test_icmpv6header.txt index dcfeb34..f54dd5c 100644 --- a/test/CMakeLists.test_icmpv6header.txt +++ b/test/CMakeLists.test_icmpv6header.txt @@ -3,6 +3,7 @@ add_executable(test_icmpv6header test_icmpv6header.cpp ${CMAKE_SOURCE_DIR}/src/boost_assert_handler.cpp ${CMAKE_SOURCE_DIR}/src/icmp/icmpdata.cpp + ${CMAKE_SOURCE_DIR}/src/icmp/icmpdata_pingfailreply.cpp ${CMAKE_SOURCE_DIR}/src/icmp/icmpechodata.cpp ${CMAKE_SOURCE_DIR}/src/icmp/icmpdestinationunreachabledata.cpp ${CMAKE_SOURCE_DIR}/src/icmp/icmpheader.cpp