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 void dump_packet(const std::string &data)
50 // create unique file name
51 std::stringstream temp_name;
52 temp_name << "/datastore/pingcheck.broken/icmp_";
53 time_t capture_time = time(0);
54 temp_name << capture_time;
55 temp_name << "_XXXXXX.pcap";
56 std::string temp_str = temp_name.str();
57 std::size_t temp_size = temp_str.size();
58 boost::scoped_array<char> secure_filename( new char[temp_size + 1] );
59 std::copy(temp_str.begin(), temp_str.end(), secure_filename.get());
60 secure_filename[temp_size] = '\0';
61 int fd = mkstemps(secure_filename.get(), 5); // 5 = ".pcap".length
64 GlobalLogger.warning() << "Failed to create temp file "
65 << secure_filename.get() << ": " << strerror(errno) << "!" << endl;
66 // maybe create containing directory if errno == ENOENT?
70 // create file pointer for file descriptor
71 FILE *fp = fdopen(fd, "w");
74 write_pcap_packet_data(data, fp, capture_time);
79 GlobalLogger.debug() << "Dumped a copy of the data into "
80 << secure_filename.get() << endl;
82 //-----------------------------------------------------------------------------
84 //-----------------------------------------------------------------------------
87 * @brief Creates an ICMP packet from the input stream @c std::istream.
89 * @param protocol The packet's network layer protocol, IPv4 or IPv6.
90 * @param is The input stream.
91 * @param dump_mode: 0 for no dumping of packet data, 1 for dump if packet
92 * creation failed and 2 for dumping always
94 * @return An ICMP Packet object.
96 IcmpPacketItem IcmpPacketFactory::create_icmp_packet(
97 const icmp::socket::protocol_type &protocol,
102 IcmpPacketItem icmp_packet;
104 if ( icmp::v4() == protocol || icmp::v6() == protocol )
105 icmp_packet.reset( new IcmpPacket( protocol ) );
108 GlobalLogger.warning() << "ICMP packet creation failed: "
109 << "Unknown protocol, expect ICMP v4 or v6!" << endl;
110 icmp_packet.reset(); // --> (!icmp_packet) is true
114 IcmpPacket::ReadReturnCode return_code;
116 // create buffer for saving data in it
117 stringbuf data_backup;
118 bool have_backup = false;
120 // create backup and parse data
123 // read packet from stream, possibly copying data first
126 // read all data into backup
127 ostream backup_filler(&data_backup);
128 backup_filler << is.rdbuf();
130 // create a new stream from backup buffer
131 // and use that in read
132 istream is_2(&data_backup);
134 return_code = icmp_packet->read( is_2 );
137 return_code = icmp_packet->read( is );
139 catch ( const std::exception &ex )
141 GlobalLogger.notice() << "Exception during ICMP parse: " << ex.what()
147 GlobalLogger.notice() << "Exception during ICMP parse." << std::endl;
151 if ( return_code != IcmpPacket::ReadReturnCode_OK )
153 GlobalLogger.warning() << "ICMP packet creation failed: "
154 << IcmpPacket::return_code_to_string(return_code) << endl;
155 icmp_packet.reset(); // --> (!icmp_packet) is true
156 // --> icmp_pinger will not try to continue working with this packet
158 else if ( !is.good() )
160 GlobalLogger.warning() << "ICMP packet creation failed: "
161 << "Stream not good at end of creation!" << endl;
162 icmp_packet.reset(); // --> (!icmp_packet) is true
165 // dump data if had trouble with packet or dumping is set to always
166 if ( dump_mode == 2 || ( dump_mode==1 && !icmp_packet ) )
169 dump_packet(data_backup.str());
171 GlobalLogger.warning() << "Would like to dump packet but "
172 << "exception occured before backup was created!";
176 GlobalLogger.debug() << "Read packet " << icmp_packet->to_string()
179 GlobalLogger.debug() << "Read packet failed" << endl;
185 * @brief Creates an ICMP Echo Request packet.
187 * @param protocol The packet's network layer protocol, IPv4 or IPv6.
188 * @param identifier The packet's identifier number to aid in matching Echo
189 * Replies to this Echo Request. May be zero.
190 * @param sequence_number The packet's sequence number to aid in matching Echo
191 * Replies to this Echo Request. May be zero.
193 * @return An ICMP Echo Request packet object.
195 IcmpPacketItem IcmpPacketFactory::create_icmp_packet_echo_request(
196 const icmp::socket::protocol_type &protocol,
197 const uint16_t identifier,
198 const uint16_t sequence_number
201 BOOST_ASSERT( (icmp::v4() == protocol) || (icmp::v6() == protocol) );
204 if ( icmp::v4() == protocol )
205 type = static_cast<uint8_t>(Icmpv4Type_EchoRequest);
206 else if ( icmp::v6() == protocol )
207 type = static_cast<uint8_t>(Icmpv6Type_EchoRequest);
208 // other case caught be BOOST_ASSERT above
211 IcmpHeader icmp_header( type, code );
213 IcmpDataPtr icmp_data( new IcmpEchoData( identifier, sequence_number,
216 IcmpPacketItem icmp_packet( new IcmpPacket( protocol,
219 GlobalLogger.debug() << "IcmpPacketFactory: created echo request packet"