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 <tmpfstream.hpp>
38 #include "boost_assert_handler.h"
39 #include "icmp/icmpdata.h"
40 #include "icmp/icmpheader.h"
41 #include "icmp/icmptype.h"
42 #include "icmp/icmpechodata.h"
43 #include "tools/pcap.h"
46 using boost::asio::ip::icmp;
47 using I2n::Logger::GlobalLogger;
49 //-----------------------------------------------------------------------------
51 //-----------------------------------------------------------------------------
54 DumpMode IcmpPacketFactory::PacketDumpMode = DUMP_IF_ERROR;
55 std::string IcmpPacketFactory::DumpFilePrefix = "/tmp/icmp_";
58 * @brief Creates an ICMP packet from the input stream @c std::istream.
60 * @param protocol The packet's network layer protocol, IPv4 or IPv6.
61 * @param is The input stream.
63 * @return An ICMP Packet object.
65 IcmpPacketItem IcmpPacketFactory::create_icmp_packet(
66 const icmp::socket::protocol_type &protocol,
70 IcmpPacketItem icmp_packet;
72 if ( icmp::v4() == protocol || icmp::v6() == protocol )
73 icmp_packet.reset( new IcmpPacket( protocol ) );
76 GlobalLogger.warning() << "ICMP packet creation failed: "
77 << "Unknown protocol arg, expect ICMP v4 or v6!" << endl;
78 icmp_packet.reset(); // --> (!icmp_packet) is true
82 IcmpPacket::ReadReturnCode return_code;
84 // create buffer for saving data in it
85 stringbuf data_backup;
86 bool have_backup = false;
88 // create backup and parse data
91 // read packet from stream, possibly copying data first
92 if (PacketDumpMode != DUMP_NEVER)
94 // read all data into backup
95 ostream backup_filler(&data_backup);
96 backup_filler << is.rdbuf();
98 // create a new stream from backup buffer
99 // and use that in read
100 istream is_2(&data_backup);
102 return_code = icmp_packet->read( is_2 );
105 return_code = icmp_packet->read( is );
107 if ( return_code != IcmpPacket::ReadReturnCode_OK )
109 GlobalLogger.warning() << "ICMP packet creation failed: "
110 << IcmpPacket::return_code_to_string(return_code) << endl;
111 icmp_packet.reset(); // --> (!icmp_packet) is true
112 // --> icmp_pinger will not try to continue working with this packet
114 else if ( !is.good() )
116 GlobalLogger.warning() << "ICMP packet creation failed: "
117 << "Stream not good at end of creation!" << endl;
118 icmp_packet.reset(); // --> (!icmp_packet) is true
121 // print end result within try-catch because to_string might also
124 GlobalLogger.debug() << "Read packet " << icmp_packet->to_string()
127 GlobalLogger.debug() << "Read packet failed" << endl;
130 catch ( const std::exception &ex )
132 GlobalLogger.notice() << "Exception during ICMP parse: " << ex.what()
138 GlobalLogger.notice() << "Exception during ICMP parse." << std::endl;
142 // dump data if had trouble with packet or dumping is set to always
143 if ( PacketDumpMode == DUMP_ALWAYS ||
144 ( PacketDumpMode == DUMP_IF_ERROR && !icmp_packet ) )
147 dump_packet(data_backup.str());
149 GlobalLogger.warning() << "Would like to dump packet but "
150 << "trouble occured before backup was created!";
157 * @brief Creates an ICMP Echo Request packet.
159 * @param protocol The packet's network layer protocol, IPv4 or IPv6.
160 * @param identifier The packet's identifier number to aid in matching Echo
161 * Replies to this Echo Request. May be zero.
162 * @param sequence_number The packet's sequence number to aid in matching Echo
163 * Replies to this Echo Request. May be zero.
165 * @return An ICMP Echo Request packet object.
167 IcmpPacketItem IcmpPacketFactory::create_icmp_packet_echo_request(
168 const icmp::socket::protocol_type &protocol,
169 const uint16_t identifier,
170 const uint16_t sequence_number
173 BOOST_ASSERT( (icmp::v4() == protocol) || (icmp::v6() == protocol) );
176 if ( icmp::v4() == protocol )
177 type = static_cast<uint8_t>(Icmpv4Type_EchoRequest);
178 else if ( icmp::v6() == protocol )
179 type = static_cast<uint8_t>(Icmpv6Type_EchoRequest);
180 // other case caught be BOOST_ASSERT above
183 IcmpHeader icmp_header( type, code );
185 IcmpDataPtr icmp_data( new IcmpEchoData( identifier, sequence_number,
188 IcmpPacketItem icmp_packet( new IcmpPacket( protocol,
191 GlobalLogger.debug() << "IcmpPacketFactory: created echo request packet"
198 void IcmpPacketFactory::dump_packet(const std::string &data)
200 // create unique file name
201 time_t capture_time = time(0);
202 std::stringstream temp_name;
203 temp_name << DumpFilePrefix << capture_time << ".pcap.XXXXXX";
206 I2n::tmpfstream temp_stream;
207 if ( !temp_stream.open(temp_name.str()) )
209 GlobalLogger.warning() << "Failed to create temp file "
210 << temp_name.str() << ": " << strerror(errno) << "!" << endl;
215 write_pcap_packet_data(data, temp_stream, capture_time);
218 GlobalLogger.debug() << "Dumped a copy of the ICMP data into "
219 << temp_stream.get_tmp_filename() << endl;
223 void IcmpPacketFactory::dump_packet(const IcmpPacket &packet)
225 // create unique file name
226 time_t capture_time = time(0);
227 std::stringstream temp_name;
228 temp_name << DumpFilePrefix << capture_time << ".pcap.XXXXXX";
231 I2n::tmpfstream temp_stream;
232 if ( !temp_stream.open(temp_name.str()) )
234 GlobalLogger.warning() << "Failed to create temp file "
235 << temp_name.str() << ": " << strerror(errno) << "!" << endl;
240 packet.dump(temp_stream);
243 GlobalLogger.debug() << "Dumped a copy of the packet into "
244 << temp_stream.get_tmp_filename() << endl;