congestion detection now working; also add case that if all IPs get timeout despite...
[pingcheck] / src / icmp / icmppinger.cpp
CommitLineData
0c0bb697
TJ
1// Boost pinger (c) 2011 by Guilherme Maciel Ferreira / Intra2net AG
2// Based upon work copyright (c) 2003-2010 Christopher M. Kohlhoff (ping.cpp)
3//
4// Distributed under the Boost Software License, Version 1.0.
5// (See accompanying file LICENSE_1_0.txt or copy at
6// http://www.boost.org/LICENSE_1_0.txt)
511ced78 7#include "icmp/icmppinger.h"
9c55ecd3 8
47848853 9#include <errno.h>
47848853 10
4ea9706c
GMF
11#include <ostream>
12
9c55ecd3 13#include <boost/bind.hpp>
451c9121
GMF
14#include <boost/date_time/posix_time/posix_time.hpp>
15#include <boost/date_time/posix_time/posix_time_types.hpp>
55978089
TJ
16#include <boost/uuid/uuid.hpp>
17#include <boost/uuid/uuid_generators.hpp>
41d175b0 18#include <boost/foreach.hpp>
4d7db1af 19#include <boost/system/system_error.hpp>
55978089 20
301610ca
GMF
21#include <logfunc.hpp>
22
780b0bca 23#include "boost_assert_handler.h"
af68f845 24#include "icmp/icmppacketfactory.h"
41d175b0 25#include "host/networkinterface.hpp"
4ea9706c 26
a7c2eb51 27using namespace std;
2bf8720f
GMF
28using boost::asio::const_buffers_1;
29using boost::asio::io_service;
30using boost::asio::ip::address;
31using boost::asio::ip::icmp;
cd395966 32using boost::function;
2bf8720f 33using boost::posix_time::microsec_clock;
2bf8720f 34using boost::posix_time::seconds;
e58d7507 35using boost::shared_ptr;
301610ca 36using I2n::Logger::GlobalLogger;
2d591235 37
f076f8d4
CH
38using boost::asio::ip::icmp;
39
4ea9706c 40//-----------------------------------------------------------------------------
87e525ff 41// IcmpPinger
4ea9706c
GMF
42//-----------------------------------------------------------------------------
43
1ece191b
CH
44/**
45 * @brief factory function for IcmpPingers, ensures that set_myself is set
46 *
47 * @returns a shared pointer to a Pinger
48 */
49PingerItem IcmpPinger::create(
50 const IoServiceItem io_serv,
51 const icmp::socket::protocol_type &protocol,
52 const string &source_network_interface,
53 const int echo_reply_timeout_in_sec )
54{
55 // get distributor
56 IcmpPacketDistributorItem distributor = IcmpPacketDistributor::get_distributor(
8f00b3df 57 protocol, source_network_interface, io_serv);
1ece191b
CH
58
59 // create pinger
f076f8d4 60 IcmpPinger *ptr = new IcmpPinger(io_serv, protocol, echo_reply_timeout_in_sec, distributor);
1ece191b 61 IcmpPingerItem shared_ptr_(ptr);
1ece191b
CH
62
63 // keep weak pointer to self
64 //shared_ptr_->set_myself( weak_ptr ); //Error: Pinger::set_myself is protected
d3bb7afe 65 ptr->set_myself( shared_ptr_ );
1ece191b
CH
66
67 // register in distributor
68 distributor->register_pinger(shared_ptr_);
69
70 // done, return shared ptr
71 return shared_ptr_;
72}
9ae5d9cb 73
0697580f
GMF
74/**
75 * @brief Parameterized constructor.
5b008ada 76 *
ab2cb1ef 77 * @param io_serv The one @c io_service object that controls async processing
5b008ada
GMF
78 * @param protocol The network layer protocol to use.
79 * @param source_network_interface The network interface name from where to
80 * send the packets.
81 * @param echo_reply_timeout_in_sec The amount of time to wait for a reply.
0697580f 82 */
87e525ff 83IcmpPinger::IcmpPinger(
365036be 84 const IoServiceItem io_serv,
fc3754b0 85 const icmp::socket::protocol_type &protocol,
f076f8d4
CH
86 const int echo_reply_timeout_in_sec,
87 const IcmpPacketDistributorItem distributor
ced28dc7 88) :
e0a99ac4 89 PacketDistributor( distributor ),
33f408b1 90 DestinationEndpoint(),
5b008ada 91 Protocol( protocol ),
365036be 92 IcmpPacketReceiveTimer( *io_serv ),
87e525ff 93 Identifier( 0 ),
33f408b1 94 SequenceNumber( 0 ),
a7c2eb51 95 TimeSent( microsec_clock::universal_time() ),
0697580f 96 ReplyReceived( false ),
e08ab6c9 97 EchoReplyTimeoutInSec( echo_reply_timeout_in_sec ),
24e706c4 98 PingerStatus( PingStatus_NotSent ),
20a8838c
CH
99 PingDoneCallback(),
100 LogPrefix("IcmpPinger")
4ea9706c 101{
55978089
TJ
102 // Create "unique" identifier
103 boost::uuids::random_generator random_gen;
104 boost::uuids::uuid random_tag = random_gen();
105
da44f50a
GMF
106 BOOST_ASSERT( sizeof(Identifier) <= random_tag.size() );
107 memcpy( &Identifier, random_tag.data, sizeof(Identifier) );
20a8838c
CH
108
109 LogPrefix = "IPing(no IP yet): ";
ced28dc7
GMF
110}
111
0697580f
GMF
112/**
113 * @brief Destructor.
114 */
87e525ff 115IcmpPinger::~IcmpPinger()
ced28dc7
GMF
116{
117}
118
e08ab6c9 119/**
d8b4a7e7 120 * @brief Ping a destination address from an available local source.
e08ab6c9 121 *
2035d00e 122 * @param destination_ip The address of the host to ping.
1309d0e4
GMF
123 * @param destination_port The port at the destination host to ping.
124 * @param done_handler Done handler will be called on successful ping or timeout.
125 *
126 * @return void.
b1a82632 127 */
87e525ff 128void IcmpPinger::ping(
23f51766 129 const address &destination_ip,
080ca508 130 const uint16_t /*destination_port*/, // the ICMP protocol does not use ports
9c0dcf33 131 function<void(PingStatus,long)> ping_done_callback
87e525ff 132)
ced28dc7 133{
24e706c4
TJ
134 PingDoneCallback = ping_done_callback;
135
e1e27b3c 136 // Prepare ping
c0950a05 137 set_ping_status( PingStatus_NotSent );
e1e27b3c 138
39e10382 139 set_destination_endpoint( destination_ip );
4ea9706c 140
1ece191b 141 start_send();
5c670f6b
GMF
142}
143
5a9bc2d1
CH
144void IcmpPinger::stop_pinging()
145{
20a8838c 146 GlobalLogger.debug() << LogPrefix << "stop_pinging" << endl;
f076f8d4 147
20a8838c 148 GlobalLogger.debug() << LogPrefix << "cancel timer" << endl;
f076f8d4 149 IcmpPacketReceiveTimer.cancel();
f076f8d4 150
20a8838c 151 GlobalLogger.debug() << LogPrefix << "unregister" << endl;
4aad91a4
TJ
152
153 IcmpPingerItem icmp_item = boost::static_pointer_cast<IcmpPinger>( get_myself().lock() );
154 if ( icmp_item )
155 {
156 PacketDistributor->unregister_pinger( icmp_item );
157 } else
158 {
20a8838c
CH
159 GlobalLogger.warning() << LogPrefix
160 << "weak pointer to pinger broken is empty. Huh?" << endl;
4aad91a4 161 }
5a9bc2d1
CH
162}
163
164
23f51766 165void IcmpPinger::set_destination_endpoint( const address &destination_ip )
39e10382 166{
39e10382 167 uint16_t port = 0;
23f51766 168 DestinationEndpoint = icmp::endpoint( destination_ip, port );
20a8838c
CH
169
170 // update LogPrefix
171 std::stringstream temp;
172 temp << "IPing(" << DestinationEndpoint.address().to_string() << "): ";
173 LogPrefix = temp.str();
39e10382
GMF
174}
175
ba5d41fe 176bool IcmpPinger::start_send()
4ea9706c 177{
83d87183 178 ++SequenceNumber;
5c670f6b 179
af68f845 180 IcmpPacketItem icmp_packet_echo_request = IcmpPacketFactory::create_icmp_packet_echo_request(
5b008ada 181 Protocol, Identifier, SequenceNumber );
83d87183 182
24e706c4 183 BOOST_ASSERT( PingerStatus == PingStatus_NotSent );
ba5d41fe 184 return send_echo_request( icmp_packet_echo_request );
5c670f6b
GMF
185}
186
ba5d41fe 187bool IcmpPinger::send_echo_request( const IcmpPacketItem icmp_packet )
a8d411d6 188{
cb60ed91 189 boost::asio::streambuf request_buffer;
a7c2eb51 190 ostream os( &request_buffer );
080ca508
GMF
191 if ( !icmp_packet->write( os ) )
192 {
20a8838c 193 GlobalLogger.error() << LogPrefix << "fail writing ping data." << endl;
080ca508 194 }
cb60ed91 195
a7c2eb51 196 TimeSent = microsec_clock::universal_time();
040ffdf2 197
040ffdf2
GMF
198 string dest_address_string = DestinationEndpoint.address().to_string();
199 BOOST_ASSERT( !dest_address_string.empty() );
a4049623 200
822e4991 201 // Send the request
ba5d41fe 202 size_t bytes_sent = 0;
822e4991
GMF
203 try
204 {
20a8838c 205 GlobalLogger.info() << LogPrefix << "sending ping" << endl;
822e4991 206 const_buffers_1 data = request_buffer.data();
747c13ca 207
39e10382 208 // Block until send the data
1ece191b 209 bytes_sent = PacketDistributor->get_socket()->send_to( data, DestinationEndpoint );
822e4991
GMF
210 if ( bytes_sent != buffer_size( data ) )
211 {
4d7db1af
CH
212 GlobalLogger.error() << LogPrefix << "fail sending ping data. Only"
213 << bytes_sent << " of " << buffer_size(data)
214 << " bytes were sent!" << endl;
822e4991 215 }
4d7db1af
CH
216
217 ReplyReceived = false;
218 schedule_timeout_echo_reply();
219 }
220 catch ( const boost::system::system_error &boost_err )
221 {
222 boost::system::error_code err_code = boost_err.code();
223 GlobalLogger.error() << LogPrefix << "fail sending ping data: "
224 << boost_err.what() << " (code " << err_code.value()
225 << ", category " << err_code.category().name() << ")" << endl;
226
227 // do not wait for timeout but fail at once
228 set_ping_status(PingStatus_SendFailed);
229 ReplyReceived = true; // flag for handler to leave ping status as is
230 //handle_timeout( err_code );
231 handle_timeout( boost::system::error_code() );
822e4991
GMF
232 }
233 catch ( const exception &ex )
a4049623 234 {
4d7db1af 235 GlobalLogger.error() << LogPrefix << "fail sending ping data: "
20a8838c 236 << ex.what() << endl;
4d7db1af
CH
237
238 // do not wait for timeout but fail at once
239 set_ping_status(PingStatus_SendFailed);
240 ReplyReceived = true; // flag for handler to leave ping status as is
241 handle_timeout( boost::system::error_code() );
a4049623 242 }
4d7db1af
CH
243 catch ( ... )
244 {
245 GlobalLogger.error() << LogPrefix << "fail sending ping data: "
246 << "Unknown exception" << endl;
4ea9706c 247
4d7db1af
CH
248 // do not wait for timeout but fail at once
249 set_ping_status(PingStatus_SendFailed);
250 ReplyReceived = true; // flag for handler to leave ping status as is
251 handle_timeout( boost::system::error_code() );
252 }
ba5d41fe
CH
253
254 return (bytes_sent > 0);
2210b856
GMF
255}
256
87e525ff 257void IcmpPinger::schedule_timeout_echo_reply()
2210b856
GMF
258{
259 // Wait up to N seconds for a reply.
2b5520bc
GMF
260 (void) IcmpPacketReceiveTimer.expires_at(
261 TimeSent + seconds( EchoReplyTimeoutInSec )
262 );
263 IcmpPacketReceiveTimer.async_wait(
d26dce11 264 boost::bind( &IcmpPinger::handle_timeout, this, boost::asio::placeholders::error )
040ffdf2 265 );
4ea9706c
GMF
266}
267
24e706c4
TJ
268/**
269 * @brief Gets called when the ping is finished: Either on timeout or on ping reply
270 *
96c4e7a4 271 * @return void (but calls PingDoneCallback)
24e706c4 272 **/
d26dce11 273void IcmpPinger::handle_timeout(const boost::system::error_code& error)
bd1a2231 274{
d26dce11
CH
275 if (error)
276 {
277 if ( error == boost::asio::error::operation_aborted )
278 {
279 if (! ReplyReceived)
96c4e7a4 280 {
20a8838c
CH
281 GlobalLogger.notice() << LogPrefix
282 << "Timer waiting for ICMP echo reply was cancelled!"
283 << endl;
96c4e7a4
CH
284 set_ping_status( PingStatus_FailureAsyncCancel );
285 }
20a8838c
CH
286 // otherwise probably called by IcmpPacketReceiveTimer.cancel in
287 // handle_receive_icmp_packet!
d26dce11
CH
288 }
289 else
96c4e7a4 290 {
20a8838c
CH
291 GlobalLogger.notice() << LogPrefix << "Error " << error
292 << " waiting for ICMP echo reply!" << endl;
96c4e7a4
CH
293 set_ping_status( PingStatus_FailureAsyncError );
294 }
4d7db1af
CH
295 // could check here for more details if error is forwarded from
296 // send_echo_request
d26dce11
CH
297
298 // Still continue with rest of function, so PingStatus is updated and Callback executed
299 // when timer was cancelled
300 }
96c4e7a4
CH
301 else if ( !ReplyReceived )
302 { // Check ReplyReceived since the timer handler is also called by Timer.cancel();
20a8838c 303 GlobalLogger.info() << LogPrefix << "Request timed out" << endl;
bd1a2231
GMF
304
305 set_ping_status( PingStatus_FailureTimeout );
306 }
4d7db1af 307 // otherwise assume that ping status was set already
24e706c4
TJ
308
309 // Call ping-done handler
a7b15639 310 PingDoneCallback( PingerStatus, static_cast<long>(
9c0dcf33 311 (microsec_clock::universal_time()
a7b15639 312 - TimeSent).total_microseconds()) );
bd1a2231
GMF
313}
314
bd1a2231 315
49d66375
TJ
316/**
317 * @brief Receive ICMP packets
c0950a05 318 * @param bytes_transferred Number of bytes transferred.
1ece191b 319 * @return true if packet matches a request from this pinger, false otherwise
49d66375 320 **/
1ece191b
CH
321bool IcmpPinger::handle_receive_icmp_packet(const IcmpPacketItem icmp_packet,
322 const size_t bytes_transferred )
4ea9706c 323{
e0a99ac4
CH
324 bool does_match = false;
325
d9bbc1d7 326 if ( ReplyReceived )
e0a99ac4 327 {
1ece191b
CH
328 // continue, might be an old packet
329 // or return false right away, do not want packet anyway...
e0a99ac4
CH
330 return does_match;
331 }
130a7eda
CH
332 else if ( DestinationEndpoint.address() == address() )
333 { // we have no IP set yet
130a7eda
CH
334 return does_match;
335 }
1ece191b
CH
336
337 // We can receive all ICMP packets received by the host, so we need to
338 // filter out only the echo replies that match our identifier,
339 // expected sequence number, and destination host address (receive just
340 // the ICMP packets from the host we had ping).
341
6d80c0be 342 try
d9bbc1d7 343 {
6d80c0be
CH
344 if ( icmp_packet->match_echo_reply(
345 Identifier, SequenceNumber,
346 DestinationEndpoint.address() ) )
347 {
20a8838c 348 GlobalLogger.info() << LogPrefix << "Received reply" << endl;
d9bbc1d7 349
6d80c0be
CH
350 ReplyReceived = true;
351 does_match = true;
644e2ef7 352
6d80c0be 353 icmp_packet->print( bytes_transferred, TimeSent );
c85c0309 354
6d80c0be 355 set_ping_status( PingStatus_SuccessReply );
83d87183 356
6d80c0be
CH
357 IcmpPacketReceiveTimer.cancel(); //lint !e534
358 }
359 else if ( icmp_packet->match_destination_unreachable(
360 Identifier, SequenceNumber,
361 DestinationEndpoint.address() ) )
362 {
20a8838c
CH
363 GlobalLogger.info() << LogPrefix
364 << "Received destination unreachable" << endl;
d9bbc1d7 365
6d80c0be
CH
366 ReplyReceived = true;
367 does_match = true;
644e2ef7 368
6d80c0be 369 icmp_packet->print( bytes_transferred, TimeSent );
15023b99 370
6d80c0be 371 set_ping_status( PingStatus_FailureDestinationUnreachable );
15023b99 372
6d80c0be
CH
373 IcmpPacketReceiveTimer.cancel(); //lint !e534
374 }
375 else if ( icmp_packet->match_time_exceeded(
376 Identifier, SequenceNumber,
377 DestinationEndpoint.address() ) )
378 {
20a8838c
CH
379 GlobalLogger.info() << LogPrefix
380 << "Received time exceeded" << endl;
15023b99 381
6d80c0be
CH
382 ReplyReceived = true;
383 does_match = true;
15023b99 384
6d80c0be 385 icmp_packet->print( bytes_transferred, TimeSent );
49d66375 386
6d80c0be 387 set_ping_status( PingStatus_FailureDestinationUnreachable );
2b5520bc 388
6d80c0be
CH
389 IcmpPacketReceiveTimer.cancel(); //lint !e534
390 }
391 else
392 {
20a8838c
CH
393 GlobalLogger.debug() << LogPrefix
394 << "Received packet that does not match or has wrong seq.nr"
6d80c0be
CH
395 << endl;
396 }
d9a7f2f3 397 }
6d80c0be 398 catch ( std::exception &exc)
83e1eae2 399 {
20a8838c
CH
400 GlobalLogger.warning() << LogPrefix
401 << "Caught exception in packet interpretation: " << exc.what()
6d80c0be
CH
402 << std::endl;
403 if ( IcmpPacketFactory::PacketDumpMode == DUMP_ALWAYS ||
404 IcmpPacketFactory::PacketDumpMode == DUMP_IF_ERROR )
405 IcmpPacketFactory::dump_packet(*icmp_packet);
406 does_match = true; // avoid the same procedure in all other pingers
407 }
408 catch ( ... )
409 {
20a8838c
CH
410 GlobalLogger.warning() << LogPrefix
411 << "Caught unspecified exception in packet interpretation!"
6d80c0be
CH
412 << std::endl;
413 if ( IcmpPacketFactory::PacketDumpMode == DUMP_ALWAYS ||
414 IcmpPacketFactory::PacketDumpMode == DUMP_IF_ERROR )
415 IcmpPacketFactory::dump_packet(*icmp_packet);
416 does_match = true; // avoid the same procedure in all other pingers
2b5520bc 417 }
1ece191b
CH
418
419 return does_match;
f442d5f5
GMF
420}
421
f5c0f0d0 422void IcmpPinger::set_ping_status( PingStatus ping_status )
83d87183
GMF
423{
424 PingerStatus = ping_status;
425}
f076f8d4
CH
426
427//------------------------------------------------------------------------
428// IcmpPacketDistributor
429//------------------------------------------------------------------------
430
431static const std::size_t SOCKET_BUFFER_SIZE = 65536; // 64kB
432
433typedef std::set<IcmpPingerItem>::iterator PingerListIterator;
434
435
436bool IcmpPacketDistributor::InstanceIdentifierComparator::operator() (
437 const IcmpPacketDistributor::DistributorInstanceIdentifier &a,
438 const IcmpPacketDistributor::DistributorInstanceIdentifier &b )
439 const
440{
441 if ( a.first == boost::asio::ip::icmp::v4() )
442 {
443 if ( b.first == boost::asio::ip::icmp::v4() )
444 return a.second < b.second; // v4 == v4
445 else
446 BOOST_ASSERT( b.first == boost::asio::ip::icmp::v6() );
447 return true; // a(v4) < b(b6)
448 }
449 else
450 {
451 BOOST_ASSERT( a.first == boost::asio::ip::icmp::v6() );
452
453 if ( b.first == boost::asio::ip::icmp::v4() )
454 return false; // a(v6) > b(v4)
455 else
456 BOOST_ASSERT( b.first == boost::asio::ip::icmp::v6() );
457 return a.second < b.second; // v6 == v6
458 }
459}
460
461//-----------------------------------------------------------------------------
462// Definition of IcmpPacketDistributor
463//-----------------------------------------------------------------------------
464
465IcmpPacketDistributor::map_type IcmpPacketDistributor::Instances; // initialize
466
467
468IcmpPacketDistributorItem IcmpPacketDistributor::get_distributor(
469 const icmp::socket::protocol_type &protocol,
470 const std::string &network_interface,
471 const IoServiceItem io_serv )
472{
473 IcmpPacketDistributor::DistributorInstanceIdentifier identifier(
474 protocol, network_interface);
475
476 // check if there is an instance for this protocol and interface
477 if ( Instances.count(identifier) == 0 )
478 { // need to create an instance for this protocol and network interface
8f00b3df
CH
479 std::string protocol_str;
480 if (protocol == icmp::v4())
481 protocol_str = "ICMPv4";
482 else if (protocol == icmp::v6())
483 protocol_str = "ICMPv6";
484 else
485 protocol_str = "unknown protocol!";
486
f076f8d4 487 GlobalLogger.info() << "Creating IcmpPacketDistributor for interface "
8f00b3df
CH
488 << network_interface << " and protocol "
489 << protocol_str << std::endl;
f076f8d4
CH
490 IcmpPacketDistributorItem new_instance( new IcmpPacketDistributor(
491 protocol, network_interface, io_serv ) );
492 Instances[identifier] = new_instance;
493 }
494
495 BOOST_ASSERT( Instances.count(identifier) == 1 );
496
497 // return the one instance for this protocol and interface
498 return Instances[identifier];
499}
500
501
502IcmpPacketDistributorItem IcmpPacketDistributor::get_distributor(
503 const icmp::socket::protocol_type &protocol,
504 const std::string &network_interface )
505{
506 IcmpPacketDistributor::DistributorInstanceIdentifier identifier(
507 protocol, network_interface);
508
509 BOOST_ASSERT( Instances.count(identifier) == 1 );
510
511 // return the one instance for this protocol and interface
512 return Instances[identifier];
513}
514
515
516IcmpPacketDistributor::IcmpPacketDistributor(
517 const icmp::socket::protocol_type &protocol,
518 const std::string &network_interface,
519 const IoServiceItem io_serv ):
520 Protocol( protocol ),
e0a99ac4 521 Socket( new icmp::socket(*io_serv, protocol) ),
f076f8d4
CH
522 ReplyBuffer(),
523 PingerList()
524{
7edd33cf
CH
525 // set TTL for testing
526 //const boost::asio::ip::unicast::hops option( 3 );
527 //Socket->set_option(option);
528
f076f8d4
CH
529 NetworkInterface<icmp::socket, boost::asio::ip::icmp>
530 NetInterface( network_interface, *Socket );
531
532 if ( !NetInterface.bind() )
533 {
534 GlobalLogger.error()
535 << "Trouble creating IcmpPacketDistributor for interface "
536 << network_interface// << " and protocol " << protocol
537 << ": could not bind the socket with the local interface. "
538 << ::strerror( errno ) << std::endl;
539 }
540
541 register_receive_handler();
542}
543
544
545void IcmpPacketDistributor::register_receive_handler()
546{
547 // wait for reply, prepare buffer to receive up to SOCKET_BUFFER_SIZE bytes
548 Socket->async_receive(
549 ReplyBuffer.prepare( SOCKET_BUFFER_SIZE ),
550 boost::bind( &IcmpPacketDistributor::handle_receive, this,
551 boost::asio::placeholders::error,
552 boost::asio::placeholders::bytes_transferred )
553 );
554}
555
556void IcmpPacketDistributor::handle_receive(
557 const boost::system::error_code &error,
558 const size_t &bytes_transferred )
559{
560 if ( error )
561 {
562 GlobalLogger.warning()
563 << ": Received error " << error
564 << " in ICMP packet distributor; end handler and schedule another.";
565 register_receive_handler();
566 return;
567 }
568
569 // The actual number of bytes received is committed to the buffer so that we
570 // can extract it using a std::istream object.
571 ReplyBuffer.commit( bytes_transferred );
572
573 GlobalLogger.info() << "received packet in distributor" << std::endl;
574
e75a59e7
CH
575 std::istream is( &ReplyBuffer );
576 if ( !is )
f076f8d4 577 {
e75a59e7
CH
578 GlobalLogger.error() << "Can't handle ReplyBuffer" << std::endl;
579 return;
f076f8d4 580 }
e75a59e7
CH
581
582 // Decode the reply packet.
583 IcmpPacketItem icmp_packet = IcmpPacketFactory::create_icmp_packet(
584 Protocol, is );
585 if ( !icmp_packet )
7edd33cf 586 {
e75a59e7
CH
587 GlobalLogger.warning() << "Ignoring broken ICMP packet"
588 << std::endl;
7edd33cf 589 }
e75a59e7 590 else
f076f8d4 591 {
e75a59e7
CH
592 GlobalLogger.debug() << "Succesfully parsed ICMP packet"
593 << std::endl;
594
595 // check which pinger wants this packet
596 bool packet_matches = false;
597 BOOST_FOREACH( const IcmpPingerItem &pinger, PingerList )
598 {
599 packet_matches = pinger->handle_receive_icmp_packet(
600 icmp_packet, bytes_transferred);
601 if (packet_matches)
602 break;
603 }
604 if (!packet_matches)
605 GlobalLogger.info() << "Packet did not match any pinger"
6d80c0be 606 << std::endl;
f076f8d4
CH
607 }
608
609 // re-register receive handler
610 register_receive_handler();
611}
612
613bool IcmpPacketDistributor::register_pinger( const IcmpPingerItem &new_pinger )
614{
615 std::pair<PingerListIterator, bool> result = PingerList.insert(new_pinger);
616 bool was_new = result.second;
617 if (was_new)
618 GlobalLogger.info() << "Register new pinger with IcmpPacketDistributor"
619 << std::endl;
620 else
621 GlobalLogger.warning()
622 << "Pinger to register was already known in IcmpPacketDistributor"
623 << std::endl;
624 return was_new;
625}
626
627
628bool IcmpPacketDistributor::unregister_pinger( const IcmpPingerItem &old_pinger )
629{
630 int n_erased = PingerList.erase(old_pinger);
631 bool was_erased = n_erased > 0;
632 if (was_erased)
633 GlobalLogger.info() << "Removed pinger from IcmpPacketDistributor"
634 << std::endl;
635 else
636 GlobalLogger.warning()
637 << "Could not find pinger to remove from IcmpPacketDistributor"
638 << std::endl;
639 return was_erased;
640}
641
642/**
643 * @brief for all instances: close sockets, unregister all pingers
644 */
645void IcmpPacketDistributor::clean_up_all()
646{
647 BOOST_FOREACH( IcmpPacketDistributor::map_type::value_type &instance,
648 Instances )
1ea5fa63 649 {
f076f8d4 650 instance.second->clean_up();
1ea5fa63 651 }
f076f8d4
CH
652
653 Instances.clear();
654}
655
656void IcmpPacketDistributor::clean_up()
657{
fd62d09f 658 if (PingerList.size() == 0)
49b82a1d
CH
659 GlobalLogger.info() << "All IcmpPingers have de-registered"
660 << std::endl;
fd62d09f 661 else
f076f8d4
CH
662 GlobalLogger.warning() << "There were still " << PingerList.size()
663 << " pingers registered in IcmpPacketDistributor!" << std::endl;
664 PingerList.clear();
665
666 boost::system::error_code error;
667 //Socket->shutdown(icmp::socket::shutdown_both, error); //both=send&receive
668 //if ( error )
669 // GlobalLogger.warning() << "Received error " << error
670 // << " when shutting down ICMP socket";
671 // always gave an error system:9 (probably EBADF: Bad file descriptor)
672
673 Socket->close(error);
674 if ( error )
675 GlobalLogger.warning() << "Received error " << error
676 << " when closing ICMP socket";
677}
678
679IcmpPacketDistributor::~IcmpPacketDistributor()
680{
681 GlobalLogger.info() << "Destroying IcmpPacketDistributor" << std::endl;
682}
683
684SocketItem IcmpPacketDistributor::get_socket() const
685{
686 return Socket;
687}