2 The software in this package is distributed under the GNU General
3 Public License version 2 (with a special exception described below).
5 A copy of GNU General Public License (GPL) is included in this distribution,
6 in the file COPYING.GPL.
8 As a special exception, if other files instantiate templates or use macros
9 or inline functions from this file, or you compile this file and link it
10 with other works to produce a work based on this file, this file
11 does not by itself cause the resulting work to be covered
12 by the GNU General Public License.
14 However the source code for this file must still be made available
15 in accordance with section (3) of the GNU General Public License.
17 This exception does not invalidate any other reasons why a work based
18 on this file might be covered by the GNU General Public License.
20 Christian Herdtweck, Intra2net AG 2015
21 Based on an example in Boost Documentation (by Christopher M. Kohlhoff)
22 and adaptation by Guilherme M. Ferreira
25 #include "icmp/icmppacketfactory.h"
27 // for dumping packets
33 #include <boost/scoped_array.hpp>
35 #include <logfunc.hpp>
36 #include <filefunc.hxx>
37 #include <tmpfstream.hpp>
39 #include "boost_assert_handler.h"
40 #include "icmp/icmpdata.h"
41 #include "icmp/icmpheader.h"
42 #include "icmp/icmptype.h"
43 #include "icmp/icmpechodata.h"
44 #include "tools/pcap.h"
47 using boost::asio::ip::icmp;
48 using I2n::Logger::GlobalLogger;
50 //-----------------------------------------------------------------------------
52 //-----------------------------------------------------------------------------
55 DumpMode IcmpPacketFactory::PacketDumpMode = DUMP_ALWAYS; //DUMP_IF_ERROR;
56 std::string IcmpPacketFactory::DumpFilePrefix =
57 "/datastore/pingcheck.broken/icmp_";
60 * @brief Creates an ICMP packet from the input stream @c std::istream.
62 * @param protocol The packet's network layer protocol, IPv4 or IPv6.
63 * @param is The input stream.
65 * @return An ICMP Packet object.
67 IcmpPacketItem IcmpPacketFactory::create_icmp_packet(
68 const icmp::socket::protocol_type &protocol,
72 IcmpPacketItem icmp_packet;
74 if ( icmp::v4() == protocol || icmp::v6() == protocol )
75 icmp_packet.reset( new IcmpPacket( protocol ) );
78 GlobalLogger.warning() << "ICMP packet creation failed: "
79 << "Unknown protocol arg, expect ICMP v4 or v6!" << endl;
80 icmp_packet.reset(); // --> (!icmp_packet) is true
84 IcmpPacket::ReadReturnCode return_code;
86 // create buffer for saving data in it
87 stringbuf data_backup;
88 bool have_backup = false;
90 // create backup and parse data
93 // read packet from stream, possibly copying data first
94 if (PacketDumpMode != DUMP_NEVER)
96 // read all data into backup
97 ostream backup_filler(&data_backup);
98 backup_filler << is.rdbuf();
100 // create a new stream from backup buffer
101 // and use that in read
102 istream is_2(&data_backup);
104 return_code = icmp_packet->read( is_2 );
107 return_code = icmp_packet->read( is );
109 if ( return_code != IcmpPacket::ReadReturnCode_OK )
111 GlobalLogger.warning() << "ICMP packet creation failed: "
112 << IcmpPacket::return_code_to_string(return_code) << endl;
113 icmp_packet.reset(); // --> (!icmp_packet) is true
114 // --> icmp_pinger will not try to continue working with this packet
116 else if ( !is.good() )
118 GlobalLogger.warning() << "ICMP packet creation failed: "
119 << "Stream not good at end of creation!" << endl;
120 icmp_packet.reset(); // --> (!icmp_packet) is true
123 // print end result within try-catch because to_string might also
126 GlobalLogger.debug() << "Read packet " << icmp_packet->to_string()
129 GlobalLogger.debug() << "Read packet failed" << endl;
132 catch ( const std::exception &ex )
134 GlobalLogger.notice() << "Exception during ICMP parse: " << ex.what()
140 GlobalLogger.notice() << "Exception during ICMP parse." << std::endl;
144 // dump data if had trouble with packet or dumping is set to always
145 if ( PacketDumpMode == DUMP_ALWAYS ||
146 ( PacketDumpMode == DUMP_IF_ERROR && !icmp_packet ) )
149 dump_packet(data_backup.str());
151 GlobalLogger.warning() << "Would like to dump packet but "
152 << "trouble occured before backup was created!";
159 * @brief Creates an ICMP Echo Request packet.
161 * @param protocol The packet's network layer protocol, IPv4 or IPv6.
162 * @param identifier The packet's identifier number to aid in matching Echo
163 * Replies to this Echo Request. May be zero.
164 * @param sequence_number The packet's sequence number to aid in matching Echo
165 * Replies to this Echo Request. May be zero.
167 * @return An ICMP Echo Request packet object.
169 IcmpPacketItem IcmpPacketFactory::create_icmp_packet_echo_request(
170 const icmp::socket::protocol_type &protocol,
171 const uint16_t identifier,
172 const uint16_t sequence_number
175 BOOST_ASSERT( (icmp::v4() == protocol) || (icmp::v6() == protocol) );
178 if ( icmp::v4() == protocol )
179 type = static_cast<uint8_t>(Icmpv4Type_EchoRequest);
180 else if ( icmp::v6() == protocol )
181 type = static_cast<uint8_t>(Icmpv6Type_EchoRequest);
182 // other case caught be BOOST_ASSERT above
185 IcmpHeader icmp_header( type, code );
187 IcmpDataPtr icmp_data( new IcmpEchoData( identifier, sequence_number,
190 IcmpPacketItem icmp_packet( new IcmpPacket( protocol,
193 GlobalLogger.debug() << "IcmpPacketFactory: created echo request packet"
200 void IcmpPacketFactory::dump_packet(const std::string &data)
202 // create unique file name
203 time_t capture_time = time(0);
204 std::stringstream temp_name;
205 temp_name << DumpFilePrefix << capture_time << ".pcap.XXXXXX";
207 // check if directory exists
208 if ( !I2n::path_exists( I2n::dirname(temp_name.str()) ) )
210 GlobalLogger.debug() << "Not saving packet data because directory "
211 << I2n::dirname(temp_name.str()) << " does not exist";
216 I2n::tmpfstream temp_stream;
217 if ( !temp_stream.open(temp_name.str()) )
219 GlobalLogger.warning() << "Failed to create temp file "
220 << temp_name.str() << ": " << strerror(errno) << "!" << endl;
225 write_pcap_packet_data(data, temp_stream, capture_time);
228 GlobalLogger.debug() << "Dumped a copy of the ICMP data into "
229 << temp_stream.get_tmp_filename() << endl;
233 void IcmpPacketFactory::dump_packet(const IcmpPacket &packet)
235 // create unique file name
236 time_t capture_time = time(0);
237 std::stringstream temp_name;
238 temp_name << DumpFilePrefix << capture_time << ".pcap.XXXXXX";
240 // check if directory exists
241 if ( !I2n::path_exists( I2n::dirname(temp_name.str()) ) )
243 GlobalLogger.debug() << "Not saving packet data because directory "
244 << I2n::dirname(temp_name.str()) << " does not exist";
249 I2n::tmpfstream temp_stream;
250 if ( !temp_stream.open(temp_name.str()) )
252 GlobalLogger.warning() << "Failed to create temp file "
253 << temp_name.str() << ": " << strerror(errno) << "!" << endl;
258 packet.dump(temp_stream);
261 GlobalLogger.debug() << "Dumped a copy of the packet into "
262 << temp_stream.get_tmp_filename() << endl;