also added function to get ttl of original request IP packet but unfortunately that always returns 1 if time exceeded :-(
Also improved error reporting in ICMP packet parsing process
*/
#include "icmp/icmpdata_pingfailreply.h"
+#include "boost_assert_handler.h"
+
+#include <logfunc.hpp>
+using I2n::Logger::GlobalLogger;
IcmpData_PingFailReply::IcmpData_PingFailReply(const std::size_t size_arg)
uint16_t IcmpData_PingFailReply::get_icmp_identifier() const
-{ return get_icmp_request_data(5); }
+{ return get_icmp_request_data(4); }
uint16_t IcmpData_PingFailReply::get_icmp_sequence_number() const
-{ return get_icmp_request_data(7); }
-
+{ return get_icmp_request_data(6); }
+
+/**
+ * @brief access a given byte within request ICMP data
+ *
+ * first tests whether data actually IS a reply for an ICMP request and what
+ * IP version this is, then access the given byte and byte+1 within request
+ * ICMP header/data
+ *
+ * @returns 2-byte value based on byte and byte+1 in icmp data
+ */
uint16_t IcmpData_PingFailReply::get_icmp_request_data(
- const int data_offset) const
+ const int icmp_start_byte) 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;
+ bool is_icmp = false;
int offset = 4; // the 4 uninteresting bytes we need to skip
- if (IcmpData::raw_data[offset+0] == 4) // IPv4
+ uint8_t version_with_ihl = static_cast<uint8_t>(IcmpData::raw_data[offset]);
+ uint8_t version = (version_with_ihl & 0xF0) >> 4;
+
+ if (version == 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
+ else if (version == 6) // IPv6
{
is_icmp = IcmpData::raw_data[offset+6] == 58;
offset += 40; // 40 byte for IPv6 header
}
+ else
+ {
+ GlobalLogger.error() << "Request IP header is neither IPv4 nor v6!"
+ << std::endl;
+ BOOST_ASSERT( !"Source IP header is neither IPv4 nor v6!" );
+ }
if ( !is_icmp )
return static_cast<uint16_t>(-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);
+ // the IP header. Inside the icmp packet we need given byte offset
+ return IcmpData::raw_data.decode16(offset+icmp_start_byte,
+ offset+icmp_start_byte+1);
+}
+
+/**
+ * copying code from ip header parsing ...
+ * If we need more functions of this sort, could create a data stream from
+ * raw_data, read a IpHeader from it and get info from that
+ *
+ * Unfortunately, the value in packet is not the original one, but the one
+ * change en route to destination, so in case of TimeExceeded, it is always 1
+ */
+uint8_t IcmpData_PingFailReply::get_ip_ttl() const
+{
+ int offset = 4; // the 4 uninteresting bytes we need to skip
+ uint8_t version_with_ihl = static_cast<uint8_t>(IcmpData::raw_data[offset]);
+ uint8_t version = (version_with_ihl & 0xF0) >> 4;
+
+ if (version == 4) // IPv4
+ offset += 8; // byte 8 within IP header
+ else if (version == 6) // IPv6
+ offset += 7;
+ else
+ {
+ GlobalLogger.error() << "Request IP header is neither IPv4 nor v6!"
+ << std::endl;
+ BOOST_ASSERT( !"Source IP header is neither IPv4 nor v6!" );
+ }
+
+ return IcmpData::raw_data[offset];
}
// (created using vim -- the world's best text editor)
uint16_t get_icmp_sequence_number() const;
+ uint8_t get_ip_ttl() const;
+
// not implementing print nor to_string
protected:
- uint16_t get_icmp_request_data(const int data_offset) const;
+ uint16_t get_icmp_request_data(const int icmp_start_byte) const;
bool match_ping_request(const uint16_t identifier,
const uint16_t sequence_number) const;
else if ( icmp::v6() == protocol )
ip_head_ptr.reset( new Ipv6Header() );
else // pingcheck-type throwing of exceptions:
+ {
+ GlobalLogger.error() << "Invalid IP version, need 4 or 6!";
BOOST_ASSERT( !"Invalid IP version, need 4 or 6!" );
+ }
}
// IP header is created with only 0s, is not used in write, anyway
else if ( icmp::v6() == protocol )
ip_head_ptr.reset( new Ipv6Header() );
else // pingcheck-type throwing of exceptions:
+ {
+ GlobalLogger.error() << "Invalid IP version, need 4 or 6!";
BOOST_ASSERT( !"Invalid IP version, need 4 or 6!" );
+ }
// create checksum
icmp_header.calc_checksum( icmp_data_ptr->calc_checksum_part() );
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);
+ return icmp_data_ptr->match_time_exceeded(identifier, sequence_number);
+ // do not test ip since probably comes from router on route to dest. addr
}
bool IcmpPacket::match_echo_reply(const uint16_t identifier,
{
std::stringstream buf;
buf << "[IcmpPacket: ";
- buf << ip_head_ptr->to_string() << ", ";
+ if (ip_head_ptr)
+ buf << ip_head_ptr->to_string() << ", ";
+ else
+ buf << "no ip header, ";
+
buf << icmp_header.to_string() << ", ";
- buf << icmp_data_ptr->to_string();
+
+ if (icmp_data_ptr)
+ buf << icmp_data_ptr->to_string();
+ else
+ buf << "no icmp data";
buf << "]";
return buf.str();
}
if ( dump_mode == 2 || ( dump_mode==1 && !icmp_packet ) )
dump_packet(data_backup.str());
+ if (icmp_packet)
+ GlobalLogger.debug() << "Read packet " << icmp_packet->to_string()
+ << endl;
+ else
+ GlobalLogger.debug() << "Read packet failed" << endl;
+
return icmp_packet;
}
IcmpPacketItem icmp_packet( new IcmpPacket( protocol,
icmp_header,
icmp_data ) );
- GlobalLogger.debug() << "IcmpPacketFactory: created "
- << icmp_packet->to_string() << std::endl;
+ GlobalLogger.debug() << "IcmpPacketFactory: created echo request packet"
+ << std::endl;
return icmp_packet;
}
{
// continue, might be an old packet
// or return false right away, do not want packet anyway...
- GlobalLogger.info()
+ GlobalLogger.debug()
<< DestinationEndpoint.address().to_string()
<< ": Not interested in packets since we already got a reply"
<< endl;
}
else
{
- GlobalLogger.info()
+ GlobalLogger.debug()
<< DestinationEndpoint.address().to_string()
- << ": Received packet that does not match or is old" << endl;
+ << ": Received packet that does not match or has wrong seq.nr"
+ << endl;
}
return does_match;
ReplyBuffer(),
PingerList()
{
+ // set TTL for testing
+ //const boost::asio::ip::unicast::hops option( 3 );
+ //Socket->set_option(option);
+
NetworkInterface<icmp::socket, boost::asio::ip::icmp>
NetInterface( network_interface, *Socket );
break;
}
if (!packet_matches)
- GlobalLogger.warning() << "Packet did not match any pinger"
+ GlobalLogger.info() << "Packet did not match any pinger"
<< std::endl;
}
}
+ catch ( const std::exception &ex )
+ {
+ GlobalLogger.notice() << "Exception during ICMP parse: " << ex.what()
+ << std::endl;
+ }
catch ( ... )
{
GlobalLogger.notice() << "Exception during ICMP parse." << std::endl;
GlobalLogger.info() << "Time exceeded pinging " << remote_address
<< " (icmp_seq=" << sequence_number << ")!"
<< std::endl;
+ // TTL given as arg is the one of the return packet, so not of interest
+ // IcmpData_PingFailReply::get_ip_ttl() always gives 1
}
std::string IcmpTimeExceededData::to_string() const
{
- return "[TimeExceededData]";
+ std::stringstream buf;
+ buf << "[TimeExceededData: ID=" << std::showbase << std::hex
+ << IcmpData_PingFailReply::get_icmp_identifier() << ",seq.nr="
+ << IcmpData_PingFailReply::get_icmp_sequence_number() << "]";
+
+ return buf.str();
}
// (created using vim -- the world's best text editor)