Commit | Line | Data |
---|---|---|
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 | |
46 | using namespace std; | |
fc3754b0 | 47 | using boost::asio::ip::icmp; |
0fb1a712 GMF |
48 | using I2n::Logger::GlobalLogger; |
49 | ||
9765a83d | 50 | //----------------------------------------------------------------------------- |
0fb1a712 GMF |
51 | // IcmpPacketFactory |
52 | //----------------------------------------------------------------------------- | |
53 | ||
ed614187 | 54 | // set default value |
792f27a1 | 55 | DumpMode IcmpPacketFactory::PacketDumpMode = DUMP_IF_ERROR; |
6137bcf5 CH |
56 | std::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 | 67 | IcmpPacketItem 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 | 169 | IcmpPacketItem 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 |
200 | void 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 | { | |
210 | GlobalLogger.debug() << "Not saving packet data because directory " | |
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 | ||
233 | void 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 | { | |
243 | GlobalLogger.debug() << "Not saving packet data because directory " | |
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 | } |