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
+ // first check whether that IP packet contains an ICMP message at all
int offset = get_icmp_data_offset();
if (offset == -1)
*
* 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
+ *
+ * @throws boost assertion if data does not seem to start with an IP v4/6 header
*/
uint8_t IcmpData_PingFailReply::get_ip_ttl() const
{
/** @brief get byte index of start of ICMP request data after IP header
*
- * @returns -1 if this data is not a
- * @throws boost assertion if data does not start with an IP header of version
- * 4 or 6
+ * @returns -1 if this data does not contain a ICMP request packet
+ * @throws boost assertion if data does not seem to start with an IP v4/6 header
*/
int IcmpData_PingFailReply::get_icmp_data_offset() const
{
}
else
{
- GlobalLogger.error() << "Request IP header is neither IPv4 nor v6!"
- << std::endl;
- BOOST_ASSERT( !"Source IP header is neither IPv4 nor v6!" );
+ GlobalLogger.error()
+ << "Icmp data does not seem to start with IPv4/6 header!"
+ << std::endl;
+ BOOST_ASSERT( !"Icmp data does not seem to start with IPv4/6 header!" );
}
if (is_icmp)
bool IcmpEchoData::match_time_exceeded(
const uint16_t identifier,
- const uint16_t sequence_number) const
+ const uint16_t sequence_number,
+ const address &destination_address) const
{ return false; }
const uint16_t sequence_number) const;
bool match_time_exceeded(const uint16_t identifier,
- const uint16_t sequence_number) const;
+ const uint16_t sequence_number,
+ const address &destination_address) const;
uint16_t get_identifier() const;
#include "icmp/icmpechodata.h"
#include "icmp/icmpdestinationunreachabledata.h"
#include "icmp/icmptimeexceededdata.h"
+#include "icmp/icmppacketfactory.h"
using I2n::Logger::GlobalLogger;
else
{ // unspecified icmp data type, will consume right amount of data
// from stream but return false for match_echo_request and
- // match_destination_unreachable
+ // match_destination_unreachable
icmp_data_ptr.reset( new IcmpData(
static_cast<std::size_t>(data_length)) );
}
return !os.fail();
}
+/** @brief like write but also writes the IP header
+ *
+ * this is required in packet dumping for debugging
+ */
+bool IcmpPacket::dump( std::ostream &os ) const
+{
+ os.clear();
+
+ ip_head_ptr->write(os);
+ icmp_header.write(os);
+ icmp_data_ptr->write(os);
+
+ return !os.fail();
+}
+
/**
* @brief create packet from data in @a is; calls IcmpPacket::read
*
}
+/**
+ * @brief returns a short string representation of this packet
+ *
+ * @throws some kind of exception, possibly a runtime_error cause by a failed
+ * BOOST_ASSERT
+ */
std::string IcmpPacket::to_string() const
{
std::stringstream buf;
ReadReturnCode read(std::istream &is);
bool write(std::ostream &os) const;
+ bool dump( std::ostream &os) const;
std::string to_string() const;
using boost::asio::ip::icmp;
using I2n::Logger::GlobalLogger;
-void dump_packet(const std::string &data)
-{
- // create unique file name
- std::stringstream temp_name;
- temp_name << IcmpPacketFactory::DumpFilePrefix;
- time_t capture_time = time(0);
- temp_name << capture_time;
- temp_name << "_XXXXXX.pcap";
- std::string temp_str = temp_name.str();
- std::size_t temp_size = temp_str.size();
- boost::scoped_array<char> secure_filename( new char[temp_size + 1] );
- std::copy(temp_str.begin(), temp_str.end(), secure_filename.get());
- secure_filename[temp_size] = '\0';
- int fd = mkstemps(secure_filename.get(), 5); // 5 = ".pcap".length
- if (fd == -1)
- {
- GlobalLogger.warning() << "Failed to create temp file "
- << secure_filename.get() << ": " << strerror(errno) << "!" << endl;
- // maybe create containing directory if errno == ENOENT?
- return;
- }
-
- // create file pointer for file descriptor
- FILE *fp = fdopen(fd, "w");
-
- // dump data
- write_pcap_packet_data(data, fp, capture_time);
-
- // clean up
- fclose(fp);
- close(fd);
- GlobalLogger.debug() << "Dumped a copy of the data into "
- << secure_filename.get() << endl;
-}
//-----------------------------------------------------------------------------
// IcmpPacketFactory
//-----------------------------------------------------------------------------
}
else
return_code = icmp_packet->read( is );
+
+ if ( return_code != IcmpPacket::ReadReturnCode_OK )
+ {
+ GlobalLogger.warning() << "ICMP packet creation failed: "
+ << IcmpPacket::return_code_to_string(return_code) << endl;
+ icmp_packet.reset(); // --> (!icmp_packet) is true
+ // --> icmp_pinger will not try to continue working with this packet
+ }
+ else if ( !is.good() )
+ {
+ GlobalLogger.warning() << "ICMP packet creation failed: "
+ << "Stream not good at end of creation!" << endl;
+ icmp_packet.reset(); // --> (!icmp_packet) is true
+ }
+
+ // print end result within try-catch because to_string might also
+ // throw exception
+ if (icmp_packet)
+ GlobalLogger.debug() << "Read packet " << icmp_packet->to_string()
+ << endl;
+ else
+ GlobalLogger.debug() << "Read packet failed" << endl;
+
}
catch ( const std::exception &ex )
{
icmp_packet.reset();
}
- if ( return_code != IcmpPacket::ReadReturnCode_OK )
- {
- GlobalLogger.warning() << "ICMP packet creation failed: "
- << IcmpPacket::return_code_to_string(return_code) << endl;
- icmp_packet.reset(); // --> (!icmp_packet) is true
- // --> icmp_pinger will not try to continue working with this packet
- }
- else if ( !is.good() )
- {
- GlobalLogger.warning() << "ICMP packet creation failed: "
- << "Stream not good at end of creation!" << endl;
- icmp_packet.reset(); // --> (!icmp_packet) is true
- }
-
// dump data if had trouble with packet or dumping is set to always
if ( PacketDumpMode == DUMP_ALWAYS ||
( PacketDumpMode == DUMP_IF_ERROR && !icmp_packet ) )
<< "trouble occured before backup was created!";
}
- if (icmp_packet)
- GlobalLogger.debug() << "Read packet " << icmp_packet->to_string()
- << endl;
- else
- GlobalLogger.debug() << "Read packet failed" << endl;
-
return icmp_packet;
}
return icmp_packet;
}
+
+/**
+ * @brief helper function for dump_packet methods, do not use elsewhere!
+ *
+ * creates a file descriptor and expects calling function dump_packet to deal
+ * with it (i.e. test and close); also logs a dump_packet-related text
+ *
+ * @returns file descriptor from mkstemps
+ */
+int IcmpPacketFactory::create_dump_file(const time_t &capture_time)
+{
+ std::stringstream temp_name;
+ temp_name << DumpFilePrefix;
+ temp_name << capture_time;
+ temp_name << "_XXXXXX.pcap";
+ std::string temp_str = temp_name.str();
+ std::size_t temp_size = temp_str.size();
+ boost::scoped_array<char> secure_filename( new char[temp_size + 1] );
+ std::copy(temp_str.begin(), temp_str.end(), secure_filename.get());
+ secure_filename[temp_size] = '\0';
+ int fd = mkstemps(secure_filename.get(), 5); // 5 = ".pcap".length
+ if (fd == -1)
+ GlobalLogger.warning() << "Failed to create temp file "
+ << secure_filename.get() << ": " << strerror(errno) << "!" << endl;
+ // maybe create containing directory if errno == ENOENT?
+ else
+ GlobalLogger.debug() << "Dumping a copy of the data into "
+ << secure_filename.get() << endl;
+ return fd;
+}
+
+
+void IcmpPacketFactory::dump_packet(const std::string &data)
+{
+ // create unique file name
+ time_t capture_time = time(0);
+ int fd = create_dump_file(capture_time);
+ if (fd == -1)
+ return;
+
+ // create file pointer for file descriptor
+ FILE *fp = fdopen(fd, "w");
+
+ // dump data
+ write_pcap_packet_data(data, fp, capture_time);
+
+ // clean up
+ fclose(fp);
+ close(fd);
+}
+
+void IcmpPacketFactory::dump_packet(const IcmpPacket &packet)
+{
+ // create unique file name
+ time_t capture_time = time(0);
+ int fd = create_dump_file(capture_time);
+ if (fd == -1)
+ return;
+
+ // create file pointer for file descriptor
+ FILE *fp = fdopen(fd, "w");
+
+ // create string with packet contents
+ std::stringstream data;
+ packet.dump(data);
+
+ // dump data
+ write_pcap_packet_data(data.str(), fp, capture_time);
+
+ // clean up
+ fclose(fp);
+ close(fd);
+}
#include "icmp/icmppacket.h"
-void dump_packet(const std::string &data);
-
enum DumpMode {
DUMP_NEVER = 0,
DUMP_IF_ERROR = 1,
static std::string DumpFilePrefix;
static DumpMode PacketDumpMode;
+ static void dump_packet(const std::string &data);
+ static void dump_packet(const IcmpPacket &packet);
+
static IcmpPacketItem create_icmp_packet(
const boost::asio::ip::icmp::socket::protocol_type &protocol,
std::istream &is
const uint16_t identifier,
const uint16_t sequence_number
);
+
+private:
+ static int create_dump_file(const time_t &capture_time);
};
#endif // ICMP_PACKET_FACTORY_H
// expected sequence number, and destination host address (receive just
// the ICMP packets from the host we had ping).
- if ( icmp_packet->match_echo_reply(
- Identifier, SequenceNumber,
- DestinationEndpoint.address() ) )
+ try
{
- GlobalLogger.info()
- << DestinationEndpoint.address().to_string()
- << ": Received reply" << endl;
+ if ( icmp_packet->match_echo_reply(
+ Identifier, SequenceNumber,
+ DestinationEndpoint.address() ) )
+ {
+ GlobalLogger.info()
+ << DestinationEndpoint.address().to_string()
+ << ": Received reply" << endl;
- ReplyReceived = true;
- does_match = true;
+ ReplyReceived = true;
+ does_match = true;
- icmp_packet->print( bytes_transferred, TimeSent );
+ icmp_packet->print( bytes_transferred, TimeSent );
- set_ping_status( PingStatus_SuccessReply );
+ set_ping_status( PingStatus_SuccessReply );
- IcmpPacketReceiveTimer.cancel(); //lint !e534
- }
- else if ( icmp_packet->match_destination_unreachable(
- Identifier, SequenceNumber,
- DestinationEndpoint.address() ) )
- {
- GlobalLogger.info()
- << DestinationEndpoint.address().to_string()
- << ": Received destination unreachable" << endl;
+ IcmpPacketReceiveTimer.cancel(); //lint !e534
+ }
+ else if ( icmp_packet->match_destination_unreachable(
+ Identifier, SequenceNumber,
+ DestinationEndpoint.address() ) )
+ {
+ GlobalLogger.info()
+ << DestinationEndpoint.address().to_string()
+ << ": Received destination unreachable" << endl;
- ReplyReceived = true;
- does_match = true;
+ ReplyReceived = true;
+ does_match = true;
- icmp_packet->print( bytes_transferred, TimeSent );
+ icmp_packet->print( bytes_transferred, TimeSent );
- set_ping_status( PingStatus_FailureDestinationUnreachable );
+ set_ping_status( PingStatus_FailureDestinationUnreachable );
- 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;
+ 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;
+ ReplyReceived = true;
+ does_match = true;
- icmp_packet->print( bytes_transferred, TimeSent );
+ icmp_packet->print( bytes_transferred, TimeSent );
- set_ping_status( PingStatus_FailureDestinationUnreachable );
+ set_ping_status( PingStatus_FailureDestinationUnreachable );
- IcmpPacketReceiveTimer.cancel(); //lint !e534
+ IcmpPacketReceiveTimer.cancel(); //lint !e534
+ }
+ else
+ {
+ GlobalLogger.debug()
+ << DestinationEndpoint.address().to_string()
+ << ": Received packet that does not match or has wrong seq.nr"
+ << endl;
+ }
}
- else
+ catch ( std::exception &exc)
{
- GlobalLogger.debug()
- << DestinationEndpoint.address().to_string()
- << ": Received packet that does not match or has wrong seq.nr"
- << endl;
+ GlobalLogger.warning()
+ << DestinationEndpoint.address().to_string()
+ << ": Caught exception in packet interpretation: " << exc.what()
+ << std::endl;
+ if ( IcmpPacketFactory::PacketDumpMode == DUMP_ALWAYS ||
+ IcmpPacketFactory::PacketDumpMode == DUMP_IF_ERROR )
+ IcmpPacketFactory::dump_packet(*icmp_packet);
+ does_match = true; // avoid the same procedure in all other pingers
+ }
+ catch ( ... )
+ {
+ GlobalLogger.warning()
+ << DestinationEndpoint.address().to_string()
+ << ": Caught unspecified exception in packet interpretation!"
+ << std::endl;
+ if ( IcmpPacketFactory::PacketDumpMode == DUMP_ALWAYS ||
+ IcmpPacketFactory::PacketDumpMode == DUMP_IF_ERROR )
+ IcmpPacketFactory::dump_packet(*icmp_packet);
+ does_match = true; // avoid the same procedure in all other pingers
}
return does_match;
}
if (!packet_matches)
GlobalLogger.info() << "Packet did not match any pinger"
- << std::endl;
+ << std::endl;
}
// re-register receive handler
#include "ip/ipheader.h"
#include <sstream>
+IpHeader::IpHeader(const size_t payload_size)
+ : Payload(payload_size)
+{}
+
std::string IpHeader::to_string() const
{
std::stringstream buf;
return buf.str();
}
+std::ostream &IpHeader::write(std::ostream &os) const
+{
+ return Payload.write(os);
+}
#define IPHEADER_H
#include <stdint.h>
+#include <ostream>
#include <boost/shared_ptr.hpp>
#include <boost/asio/ip/address.hpp>
+#include "host/messagepayload.h"
/**
* abstract base class of IPv4Header and IPv6Header
class IpHeader
{
public:
+ IpHeader(const size_t payload_size);
virtual boost::asio::ip::address get_source_address() const = 0;
virtual boost::asio::ip::address get_destination_address() const = 0;
virtual uint8_t get_version() const = 0;
virtual ~IpHeader() {}
virtual std::string to_string() const;
+
+ virtual std::ostream& write(std::ostream &os) const;
+
+protected:
+ MessagePayload Payload;
};
typedef boost::shared_ptr<IpHeader> IpHeaderPtr;
* @brief Default constructor.
*/
Ipv4Header::Ipv4Header() :
- Payload( Ipv4HeaderSizeInBytes_withoutOptions )
+ IpHeader( Ipv4HeaderSizeInBytes_withoutOptions )
{
// encode a 4 into the first 4 bits
Payload.encode1(0, 0, 0);
#include <boost/asio/ip/address_v4.hpp>
#include "ip/ipheader.h"
-#include "host/messagepayload.h"
//-----------------------------------------------------------------------------
// Ipv4Header
Ipv4Header &header
);
-private:
- MessagePayload Payload;
-
};
#endif // IPV4_HEADER_H
* @brief Default constructor.
*/
Ipv6Header::Ipv6Header() :
- Payload( Ipv6HeaderSizeInBytes )
+ IpHeader( Ipv6HeaderSizeInBytes )
{
// encode a 6 into the first 4 bits
Payload.encode1(0, 0, 0);
#include <boost/asio/ip/address_v6.hpp>
#include "ip/ipheader.h"
-#include "host/messagepayload.h"
//-----------------------------------------------------------------------------
// Ipv6Header
Ipv6Header &header
);
-private:
- MessagePayload Payload;
-
};
#endif // IPV6_HEADER_H
// function declarations
void init_logger();
void increase_verbosity();
-int read_packets(std::istream &input_stream, const bool, const bool, const int);
+int read_packets(std::istream &input_stream, const bool, const bool);
//------------------------------------------------------------------------------
// helper functions
{
// set default: log at level NOTICE to stderr
I2n::Logger::enable_stderr_log( true );
- I2n::Logger::set_log_level( I2n::Logger::LogLevel::Info );
+ I2n::Logger::set_log_level( I2n::Logger::LogLevel::Debug );
GlobalLogger.info() << "Logger initiated";
}
// peek at start of stream to see if there is a pcap header
bool is_pcap = check_for_pcap_header(input_stream);
if (is_pcap)
- GlobalLogger.info() << "File is pcap";
+ GlobalLogger.info() << "File is pcap --> consume first "
+ << pcap_file_header_size << " bytes";
else
GlobalLogger.info() << "File is not pcap";
if (is_pcap)
{
// assume that there is a packet header if stream had pcap header
- GlobalLogger.debug() << "Consume packet header";
+ GlobalLogger.debug() << "Consume packet header ("
+ << pcap_packet_header_size << " bytes)";
consume_pcap_packet_header(input_stream);
if ( !input_stream )
{
if (packet_type == 1)
{
- GlobalLogger.debug() << "Consume ethernet header";
+ GlobalLogger.debug() << "Consume ethernet header ("
+ << pcap_ethernet_header_size << " bytes)";
consume_pcap_ethernet_header(input_stream);
if ( !input_stream )
{
//const uint32_t pcap_magic_number = 0xa1b2c3d4;
const uint8_t pcap_magic_bytes[] = {0xa1, 0xb2, 0xc3, 0xd4};
-// pcap file header is 5 x uint32 + 2 x uint16 --> 24 bytes
-const std::streamsize pcap_file_header_size = sizeof(pcapfile_hdr_t);
-
-// pcap file header is 4 x uint32 --> 16 bytes
-const std::streamsize pcap_packet_header_size = sizeof(pcaprec_hdr_t);
-
-// pcap ethernet header is 2 x 6 byte + 2 byte --> 14 bytes
-const std::streamsize pcap_ethernet_header_size = sizeof(pcapeth_hdr_t);
-
// determine whether is raw data or pcap by peeking at first 4 bytes
bool check_for_pcap_header(std::istream &input_stream)
{
uint16_t ether_type;
} pcapeth_hdr_t;
+// pcap file header is 5 x uint32 + 2 x uint16 --> 24 bytes
+const std::streamsize pcap_file_header_size = sizeof(pcapfile_hdr_t);
+
+// pcap file header is 4 x uint32 --> 16 bytes
+const std::streamsize pcap_packet_header_size = sizeof(pcaprec_hdr_t);
+
+// pcap ethernet header is 2 x 6 byte + 2 byte --> 14 bytes
+const std::streamsize pcap_ethernet_header_size = sizeof(pcapeth_hdr_t);
+
void write_pcap_packet_data(const std::string &data,
FILE *fp,
const time_t &capture_time);