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>
37 #include "boost_assert_handler.h"
38 #include "icmp/icmpdata.h"
39 #include "icmp/icmpheader.h"
40 #include "icmp/icmptype.h"
41 #include "icmp/icmpechodata.h"
42 #include "tools/pcap.h"
45 using boost::asio::ip::icmp;
46 using I2n::Logger::GlobalLogger;
48 //-----------------------------------------------------------------------------
50 //-----------------------------------------------------------------------------
53 DumpMode IcmpPacketFactory::PacketDumpMode = DUMP_IF_ERROR;
54 std::string IcmpPacketFactory::DumpFilePrefix = "/tmp/icmp_";
57 * @brief Creates an ICMP packet from the input stream @c std::istream.
59 * @param protocol The packet's network layer protocol, IPv4 or IPv6.
60 * @param is The input stream.
62 * @return An ICMP Packet object.
64 IcmpPacketItem IcmpPacketFactory::create_icmp_packet(
65 const icmp::socket::protocol_type &protocol,
69 IcmpPacketItem icmp_packet;
71 if ( icmp::v4() == protocol || icmp::v6() == protocol )
72 icmp_packet.reset( new IcmpPacket( protocol ) );
75 GlobalLogger.warning() << "ICMP packet creation failed: "
76 << "Unknown protocol, expect ICMP v4 or v6!" << endl;
77 icmp_packet.reset(); // --> (!icmp_packet) is true
81 IcmpPacket::ReadReturnCode return_code;
83 // create buffer for saving data in it
84 stringbuf data_backup;
85 bool have_backup = false;
87 // create backup and parse data
90 // read packet from stream, possibly copying data first
91 if (PacketDumpMode != DUMP_NEVER)
93 // read all data into backup
94 ostream backup_filler(&data_backup);
95 backup_filler << is.rdbuf();
97 // create a new stream from backup buffer
98 // and use that in read
99 istream is_2(&data_backup);
101 return_code = icmp_packet->read( is_2 );
104 return_code = icmp_packet->read( is );
106 if ( return_code != IcmpPacket::ReadReturnCode_OK )
108 GlobalLogger.warning() << "ICMP packet creation failed: "
109 << IcmpPacket::return_code_to_string(return_code) << endl;
110 icmp_packet.reset(); // --> (!icmp_packet) is true
111 // --> icmp_pinger will not try to continue working with this packet
113 else if ( !is.good() )
115 GlobalLogger.warning() << "ICMP packet creation failed: "
116 << "Stream not good at end of creation!" << endl;
117 icmp_packet.reset(); // --> (!icmp_packet) is true
120 // print end result within try-catch because to_string might also
123 GlobalLogger.debug() << "Read packet " << icmp_packet->to_string()
126 GlobalLogger.debug() << "Read packet failed" << endl;
129 catch ( const std::exception &ex )
131 GlobalLogger.notice() << "Exception during ICMP parse: " << ex.what()
137 GlobalLogger.notice() << "Exception during ICMP parse." << std::endl;
141 // dump data if had trouble with packet or dumping is set to always
142 if ( PacketDumpMode == DUMP_ALWAYS ||
143 ( PacketDumpMode == DUMP_IF_ERROR && !icmp_packet ) )
146 dump_packet(data_backup.str());
148 GlobalLogger.warning() << "Would like to dump packet but "
149 << "trouble occured before backup was created!";
156 * @brief Creates an ICMP Echo Request packet.
158 * @param protocol The packet's network layer protocol, IPv4 or IPv6.
159 * @param identifier The packet's identifier number to aid in matching Echo
160 * Replies to this Echo Request. May be zero.
161 * @param sequence_number The packet's sequence number to aid in matching Echo
162 * Replies to this Echo Request. May be zero.
164 * @return An ICMP Echo Request packet object.
166 IcmpPacketItem IcmpPacketFactory::create_icmp_packet_echo_request(
167 const icmp::socket::protocol_type &protocol,
168 const uint16_t identifier,
169 const uint16_t sequence_number
172 BOOST_ASSERT( (icmp::v4() == protocol) || (icmp::v6() == protocol) );
175 if ( icmp::v4() == protocol )
176 type = static_cast<uint8_t>(Icmpv4Type_EchoRequest);
177 else if ( icmp::v6() == protocol )
178 type = static_cast<uint8_t>(Icmpv6Type_EchoRequest);
179 // other case caught be BOOST_ASSERT above
182 IcmpHeader icmp_header( type, code );
184 IcmpDataPtr icmp_data( new IcmpEchoData( identifier, sequence_number,
187 IcmpPacketItem icmp_packet( new IcmpPacket( protocol,
190 GlobalLogger.debug() << "IcmpPacketFactory: created echo request packet"
198 * @brief helper function for dump_packet methods, do not use elsewhere!
200 * creates a file descriptor and expects calling function dump_packet to deal
201 * with it (i.e. test and close); also logs a dump_packet-related text
203 * @returns file descriptor from mkstemps
205 int IcmpPacketFactory::create_dump_file(const time_t &capture_time)
207 std::stringstream temp_name;
208 temp_name << DumpFilePrefix;
209 temp_name << capture_time;
210 temp_name << "_XXXXXX.pcap";
211 std::string temp_str = temp_name.str();
212 std::size_t temp_size = temp_str.size();
213 boost::scoped_array<char> secure_filename( new char[temp_size + 1] );
214 std::copy(temp_str.begin(), temp_str.end(), secure_filename.get());
215 secure_filename[temp_size] = '\0';
216 int fd = mkstemps(secure_filename.get(), 5); // 5 = ".pcap".length
218 GlobalLogger.warning() << "Failed to create temp file "
219 << secure_filename.get() << ": " << strerror(errno) << "!" << endl;
220 // maybe create containing directory if errno == ENOENT?
222 GlobalLogger.debug() << "Dumping a copy of the data into "
223 << secure_filename.get() << endl;
228 void IcmpPacketFactory::dump_packet(const std::string &data)
230 // create unique file name
231 time_t capture_time = time(0);
232 int fd = create_dump_file(capture_time);
236 // create file pointer for file descriptor
237 FILE *fp = fdopen(fd, "w");
240 write_pcap_packet_data(data, fp, capture_time);
247 void IcmpPacketFactory::dump_packet(const IcmpPacket &packet)
249 // create unique file name
250 time_t capture_time = time(0);
251 int fd = create_dump_file(capture_time);
255 // create file pointer for file descriptor
256 FILE *fp = fdopen(fd, "w");
258 // create string with packet contents
259 std::stringstream data;
263 write_pcap_packet_data(data.str(), fp, capture_time);