/* 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/icmppacketfactory.h" // for dumping packets #include #include #include #include #include #include #include #include #include #include "boost_assert_handler.h" #include "icmp/icmpdata.h" #include "icmp/icmpheader.h" #include "icmp/icmptype.h" #include "icmp/icmpechodata.h" #include "tools/pcap.h" using namespace std; using boost::asio::ip::icmp; using I2n::Logger::GlobalLogger; //----------------------------------------------------------------------------- // IcmpPacketFactory //----------------------------------------------------------------------------- // set default value DumpMode IcmpPacketFactory::PacketDumpMode = DUMP_IF_ERROR; std::string IcmpPacketFactory::DumpFilePrefix = "/datastore/pingcheck.broken/icmp_"; /** * @brief Creates an ICMP packet from the input stream @c std::istream. * * @param protocol The packet's network layer protocol, IPv4 or IPv6. * @param is The input stream. * * @return An ICMP Packet object. */ IcmpPacketItem IcmpPacketFactory::create_icmp_packet( const icmp::socket::protocol_type &protocol, istream &is ) { IcmpPacketItem icmp_packet; if ( icmp::v4() == protocol || icmp::v6() == protocol ) icmp_packet.reset( new IcmpPacket( protocol ) ); else { GlobalLogger.warning() << "ICMP packet creation failed: " << "Unknown protocol arg, expect ICMP v4 or v6!" << endl; icmp_packet.reset(); // --> (!icmp_packet) is true return icmp_packet; } IcmpPacket::ReadReturnCode return_code; // create buffer for saving data in it stringbuf data_backup; bool have_backup = false; // create backup and parse data try { // read packet from stream, possibly copying data first if (PacketDumpMode != DUMP_NEVER) { // read all data into backup ostream backup_filler(&data_backup); backup_filler << is.rdbuf(); // create a new stream from backup buffer // and use that in read istream is_2(&data_backup); have_backup = true; return_code = icmp_packet->read( is_2 ); } 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 ) { GlobalLogger.info() << "Exception during ICMP parse: " << ex.what() << std::endl; icmp_packet.reset(); } catch ( ... ) { GlobalLogger.info() << "Exception during ICMP parse." << std::endl; icmp_packet.reset(); } // dump data if had trouble with packet or dumping is set to always if ( PacketDumpMode == DUMP_ALWAYS || ( PacketDumpMode == DUMP_IF_ERROR && !icmp_packet ) ) { if ( have_backup ) dump_packet(data_backup.str()); else GlobalLogger.warning() << "Would like to dump packet but " << "trouble occurred before backup was created!"; } return icmp_packet; } /** * @brief Creates an ICMP Echo Request packet. * * @param protocol The packet's network layer protocol, IPv4 or IPv6. * @param identifier The packet's identifier number to aid in matching Echo * Replies to this Echo Request. May be zero. * @param sequence_number The packet's sequence number to aid in matching Echo * Replies to this Echo Request. May be zero. * * @return An ICMP Echo Request packet object. */ IcmpPacketItem IcmpPacketFactory::create_icmp_packet_echo_request( const icmp::socket::protocol_type &protocol, const uint16_t identifier, const uint16_t sequence_number ) { BOOST_ASSERT( (icmp::v4() == protocol) || (icmp::v6() == protocol) ); uint8_t type; if ( icmp::v4() == protocol ) type = static_cast(Icmpv4Type_EchoRequest); else if ( icmp::v6() == protocol ) type = static_cast(Icmpv6Type_EchoRequest); // other case caught be BOOST_ASSERT above uint8_t code = 0; IcmpHeader icmp_header( type, code ); IcmpDataPtr icmp_data( new IcmpEchoData( identifier, sequence_number, "ping-message" ) ); IcmpPacketItem icmp_packet( new IcmpPacket( protocol, icmp_header, icmp_data ) ); GlobalLogger.debug() << "IcmpPacketFactory: created echo request packet" << std::endl; return icmp_packet; } void IcmpPacketFactory::dump_packet(const std::string &data) { // create unique file name time_t capture_time = time(0); std::stringstream temp_name; temp_name << DumpFilePrefix << capture_time << ".pcap.XXXXXX"; // check if directory exists if ( !I2n::path_exists( I2n::dirname(temp_name.str()) ) ) { GlobalLogger.info() << "Not saving packet data because directory " << I2n::dirname(temp_name.str()) << " does not exist"; return; } // open file I2n::tmpfstream temp_stream; if ( !temp_stream.open(temp_name.str()) ) { GlobalLogger.warning() << "Failed to create temp file " << temp_name.str() << ": " << strerror(errno) << "!" << endl; return; } // write write_pcap_packet_data(data, temp_stream, capture_time); // close GlobalLogger.debug() << "Dumped a copy of the ICMP data into " << temp_stream.get_tmp_filename() << endl; temp_stream.close(); } void IcmpPacketFactory::dump_packet(const IcmpPacket &packet) { // create unique file name time_t capture_time = time(0); std::stringstream temp_name; temp_name << DumpFilePrefix << capture_time << ".pcap.XXXXXX"; // check if directory exists if ( !I2n::path_exists( I2n::dirname(temp_name.str()) ) ) { GlobalLogger.info() << "Not saving packet data because directory " << I2n::dirname(temp_name.str()) << " does not exist"; return; } // open file I2n::tmpfstream temp_stream; if ( !temp_stream.open(temp_name.str()) ) { GlobalLogger.warning() << "Failed to create temp file " << temp_name.str() << ": " << strerror(errno) << "!" << endl; return; } // dump data packet.dump(temp_stream); // close GlobalLogger.debug() << "Dumped a copy of the packet into " << temp_stream.get_tmp_filename() << endl; temp_stream.close(); }