increse revision to 0.6.1 and log level of missing-dump-dir-message to notice
[pingcheck] / src / icmp / icmppacketfactory.cpp
CommitLineData
0fb1a712
GMF
1/*
2 The software in this package is distributed under the GNU General
3 Public License version 2 (with a special exception described below).
4
5 A copy of GNU General Public License (GPL) is included in this distribution,
6 in the file COPYING.GPL.
7
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.
13
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.
16
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.
057e6cc9
CH
19
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
0fb1a712
GMF
23 */
24
25#include "icmp/icmppacketfactory.h"
26
9765a83d 27// for dumping packets
174abc69
CH
28#include <cstring>
29#include <cstdlib>
9765a83d 30#include <ctime>
7238e876
CH
31#include <iostream>
32#include <sstream>
a92bd1ca 33#include <boost/scoped_array.hpp>
3d5eac0b 34
0fb1a712 35#include <logfunc.hpp>
6137bcf5 36#include <filefunc.hxx>
d291ad16 37#include <tmpfstream.hpp>
0fb1a712 38
780b0bca 39#include "boost_assert_handler.h"
0fb1a712 40#include "icmp/icmpdata.h"
c120ad42 41#include "icmp/icmpheader.h"
0fb1a712 42#include "icmp/icmptype.h"
c120ad42 43#include "icmp/icmpechodata.h"
688d4b27 44#include "tools/pcap.h"
0fb1a712
GMF
45
46using namespace std;
fc3754b0 47using boost::asio::ip::icmp;
0fb1a712
GMF
48using I2n::Logger::GlobalLogger;
49
9765a83d 50//-----------------------------------------------------------------------------
0fb1a712
GMF
51// IcmpPacketFactory
52//-----------------------------------------------------------------------------
53
ed614187 54// set default value
792f27a1 55DumpMode IcmpPacketFactory::PacketDumpMode = DUMP_IF_ERROR;
6137bcf5
CH
56std::string IcmpPacketFactory::DumpFilePrefix =
57 "/datastore/pingcheck.broken/icmp_";
ed614187 58
fc3754b0 59/**
46e3c23e
GMF
60 * @brief Creates an ICMP packet from the input stream @c std::istream.
61 *
62 * @param protocol The packet's network layer protocol, IPv4 or IPv6.
63 * @param is The input stream.
64 *
65 * @return An ICMP Packet object.
fc3754b0 66 */
0fb1a712 67IcmpPacketItem IcmpPacketFactory::create_icmp_packet(
fc3754b0 68 const icmp::socket::protocol_type &protocol,
ed614187 69 istream &is
0fb1a712
GMF
70)
71{
0fb1a712
GMF
72 IcmpPacketItem icmp_packet;
73
c120ad42 74 if ( icmp::v4() == protocol || icmp::v6() == protocol )
747c13ca 75 icmp_packet.reset( new IcmpPacket( protocol ) );
fc3754b0
GMF
76 else
77 {
d9bbc1d7 78 GlobalLogger.warning() << "ICMP packet creation failed: "
91aa83f9 79 << "Unknown protocol arg, expect ICMP v4 or v6!" << endl;
d9bbc1d7
CH
80 icmp_packet.reset(); // --> (!icmp_packet) is true
81 return icmp_packet;
0fb1a712
GMF
82 }
83
3d5eac0b 84 IcmpPacket::ReadReturnCode return_code;
3d5eac0b 85
7238e876
CH
86 // create buffer for saving data in it
87 stringbuf data_backup;
e75a59e7 88 bool have_backup = false;
3d5eac0b 89
e75a59e7
CH
90 // create backup and parse data
91 try
7238e876 92 {
e75a59e7 93 // read packet from stream, possibly copying data first
ed614187 94 if (PacketDumpMode != DUMP_NEVER)
e75a59e7
CH
95 {
96 // read all data into backup
97 ostream backup_filler(&data_backup);
98 backup_filler << is.rdbuf();
99
100 // create a new stream from backup buffer
101 // and use that in read
102 istream is_2(&data_backup);
103 have_backup = true;
104 return_code = icmp_packet->read( is_2 );
105 }
106 else
107 return_code = icmp_packet->read( is );
6d80c0be
CH
108
109 if ( return_code != IcmpPacket::ReadReturnCode_OK )
110 {
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
115 }
116 else if ( !is.good() )
117 {
118 GlobalLogger.warning() << "ICMP packet creation failed: "
119 << "Stream not good at end of creation!" << endl;
120 icmp_packet.reset(); // --> (!icmp_packet) is true
121 }
122
123 // print end result within try-catch because to_string might also
124 // throw exception
125 if (icmp_packet)
126 GlobalLogger.debug() << "Read packet " << icmp_packet->to_string()
127 << endl;
128 else
129 GlobalLogger.debug() << "Read packet failed" << endl;
130
e75a59e7
CH
131 }
132 catch ( const std::exception &ex )
133 {
134 GlobalLogger.notice() << "Exception during ICMP parse: " << ex.what()
135 << std::endl;
136 icmp_packet.reset();
137 }
138 catch ( ... )
139 {
140 GlobalLogger.notice() << "Exception during ICMP parse." << std::endl;
141 icmp_packet.reset();
3d5eac0b 142 }
3d5eac0b 143
174abc69 144 // dump data if had trouble with packet or dumping is set to always
ed614187
CH
145 if ( PacketDumpMode == DUMP_ALWAYS ||
146 ( PacketDumpMode == DUMP_IF_ERROR && !icmp_packet ) )
e75a59e7
CH
147 {
148 if ( have_backup )
149 dump_packet(data_backup.str());
150 else
151 GlobalLogger.warning() << "Would like to dump packet but "
ed614187 152 << "trouble occured before backup was created!";
e75a59e7 153 }
3d5eac0b 154
0fb1a712
GMF
155 return icmp_packet;
156}
157
fc3754b0 158/**
46e3c23e
GMF
159 * @brief Creates an ICMP Echo Request packet.
160 *
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.
166 *
167 * @return An ICMP Echo Request packet object.
fc3754b0 168 */
0fb1a712 169IcmpPacketItem IcmpPacketFactory::create_icmp_packet_echo_request(
fc3754b0 170 const icmp::socket::protocol_type &protocol,
0fb1a712
GMF
171 const uint16_t identifier,
172 const uint16_t sequence_number
173)
174{
fc3754b0 175 BOOST_ASSERT( (icmp::v4() == protocol) || (icmp::v6() == protocol) );
0fb1a712 176
c120ad42 177 uint8_t type;
fc3754b0 178 if ( icmp::v4() == protocol )
c120ad42 179 type = static_cast<uint8_t>(Icmpv4Type_EchoRequest);
fc3754b0 180 else if ( icmp::v6() == protocol )
c120ad42
CH
181 type = static_cast<uint8_t>(Icmpv6Type_EchoRequest);
182 // other case caught be BOOST_ASSERT above
0fb1a712 183
0fb1a712 184 uint8_t code = 0;
c120ad42 185 IcmpHeader icmp_header( type, code );
0fb1a712 186
c120ad42
CH
187 IcmpDataPtr icmp_data( new IcmpEchoData( identifier, sequence_number,
188 "ping-message" ) );
0fb1a712 189
c120ad42
CH
190 IcmpPacketItem icmp_packet( new IcmpPacket( protocol,
191 icmp_header,
192 icmp_data ) );
7edd33cf
CH
193 GlobalLogger.debug() << "IcmpPacketFactory: created echo request packet"
194 << std::endl;
0fb1a712
GMF
195
196 return icmp_packet;
197}
198
6d80c0be 199
6d80c0be
CH
200void IcmpPacketFactory::dump_packet(const std::string &data)
201{
202 // create unique file name
203 time_t capture_time = time(0);
d291ad16
CH
204 std::stringstream temp_name;
205 temp_name << DumpFilePrefix << capture_time << ".pcap.XXXXXX";
6d80c0be 206
6137bcf5
CH
207 // check if directory exists
208 if ( !I2n::path_exists( I2n::dirname(temp_name.str()) ) )
209 {
ca34b9fb 210 GlobalLogger.notice() << "Not saving packet data because directory "
6137bcf5
CH
211 << I2n::dirname(temp_name.str()) << " does not exist";
212 return;
213 }
214
d291ad16
CH
215 // open file
216 I2n::tmpfstream temp_stream;
217 if ( !temp_stream.open(temp_name.str()) )
218 {
219 GlobalLogger.warning() << "Failed to create temp file "
220 << temp_name.str() << ": " << strerror(errno) << "!" << endl;
221 return;
222 }
6d80c0be 223
d291ad16
CH
224 // write
225 write_pcap_packet_data(data, temp_stream, capture_time);
6d80c0be 226
d291ad16
CH
227 // close
228 GlobalLogger.debug() << "Dumped a copy of the ICMP data into "
229 << temp_stream.get_tmp_filename() << endl;
230 temp_stream.close();
6d80c0be
CH
231}
232
233void IcmpPacketFactory::dump_packet(const IcmpPacket &packet)
234{
235 // create unique file name
236 time_t capture_time = time(0);
d291ad16
CH
237 std::stringstream temp_name;
238 temp_name << DumpFilePrefix << capture_time << ".pcap.XXXXXX";
6d80c0be 239
6137bcf5
CH
240 // check if directory exists
241 if ( !I2n::path_exists( I2n::dirname(temp_name.str()) ) )
242 {
ca34b9fb 243 GlobalLogger.notice() << "Not saving packet data because directory "
6137bcf5
CH
244 << I2n::dirname(temp_name.str()) << " does not exist";
245 return;
246 }
247
d291ad16
CH
248 // open file
249 I2n::tmpfstream temp_stream;
250 if ( !temp_stream.open(temp_name.str()) )
251 {
252 GlobalLogger.warning() << "Failed to create temp file "
253 << temp_name.str() << ": " << strerror(errno) << "!" << endl;
254 return;
255 }
6d80c0be
CH
256
257 // dump data
d291ad16 258 packet.dump(temp_stream);
6d80c0be 259
d291ad16
CH
260 // close
261 GlobalLogger.debug() << "Dumped a copy of the packet into "
262 << temp_stream.get_tmp_filename() << endl;
263 temp_stream.close();
6d80c0be 264}