From f076f8d4511cd25a802b28284de42149ef70c646 Mon Sep 17 00:00:00 2001 From: Christian Herdtweck Date: Thu, 5 Feb 2015 16:22:14 +0100 Subject: [PATCH] got packetdistributor to work, but compiles/links only with packetdistributor inside icmppinger.h/cpp --- src/CMakeLists.txt | 3 +- src/host/pingscheduler.cpp | 8 +- src/host/pingscheduler.h | 2 - src/icmp/icmppacketdistributor.cpp | 27 ++-- src/icmp/icmppacketdistributor.h | 37 ++--- src/icmp/icmppinger.cpp | 284 +++++++++++++++++++++++++++++++++++- src/icmp/icmppinger.h | 95 +++++++++++- src/main.cpp | 4 +- 8 files changed, 411 insertions(+), 49 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2bedbd1..11ecd07 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -86,9 +86,8 @@ set(SOURCES icmp/icmpv4packet.cpp icmp/icmpv6packet.cpp icmp/icmppacket.cpp - icmp/icmppacketfactory.cpp - icmp/icmppacketdistributor.cpp icmp/icmppinger.cpp + icmp/icmppacketfactory.cpp ip/ipv4header.cpp ip/ipv6header.cpp link/linkstatus.cpp diff --git a/src/host/pingscheduler.cpp b/src/host/pingscheduler.cpp index c737a46..881976a 100644 --- a/src/host/pingscheduler.cpp +++ b/src/host/pingscheduler.cpp @@ -116,7 +116,13 @@ PingScheduler::~PingScheduler() void PingScheduler::stop_pinging() { + // stop pinger, which will probably call ping_done_handler --> re-new NextPingTimer + GlobalLogger.debug() << "scheduler: stop pinging" << endl; Ping->stop_pinging(); + + // now cancel the own timer + GlobalLogger.debug() << "scheduler: cancel timer" << endl; + NextPingTimer.cancel(); } /** @@ -147,7 +153,7 @@ void PingScheduler::start_pinging() void PingScheduler::resolve_and_ping(const boost::system::error_code &error) { if ( error ) - { + { // get here, e.g. by NextPingTimer.cancel in stop_pinging if ( error == boost::asio::error::operation_aborted ) GlobalLogger.error() << "Timer for resolve_and_ping was cancelled! Stopping" << endl; else diff --git a/src/host/pingscheduler.h b/src/host/pingscheduler.h index 8cd0b59..0dc8761 100644 --- a/src/host/pingscheduler.h +++ b/src/host/pingscheduler.h @@ -73,8 +73,6 @@ private: // void resolve_and_ping(const boost::system::error_code &error); - bool resolve_address(); - void force_address_resolution(); void ping(); void ping_done_handler(const bool ping_success); diff --git a/src/icmp/icmppacketdistributor.cpp b/src/icmp/icmppacketdistributor.cpp index 26c4178..f406a56 100644 --- a/src/icmp/icmppacketdistributor.cpp +++ b/src/icmp/icmppacketdistributor.cpp @@ -29,6 +29,7 @@ #include #include "boost_assert_handler.h" +#include "host/networkinterface.hpp" #include "icmp/icmppacketfactory.h" @@ -40,9 +41,10 @@ static const std::size_t SOCKET_BUFFER_SIZE = 65536; // 64kB typedef std::set::iterator PingerListIterator; -bool IcmpPacketDistributorInstanceIdentifierComparator::operator() ( - const DistributorInstanceIdentifier &a, - const DistributorInstanceIdentifier &b ) const +bool IcmpPacketDistributor::InstanceIdentifierComparator::operator() ( + const IcmpPacketDistributor::DistributorInstanceIdentifier &a, + const IcmpPacketDistributor::DistributorInstanceIdentifier &b ) + const { if ( a.first == boost::asio::ip::icmp::v4() ) { @@ -68,7 +70,7 @@ bool IcmpPacketDistributorInstanceIdentifierComparator::operator() ( // Definition of IcmpPacketDistributor //----------------------------------------------------------------------------- -map_type IcmpPacketDistributor::Instances; // initialize +IcmpPacketDistributor::map_type IcmpPacketDistributor::Instances; // initialize IcmpPacketDistributorItem IcmpPacketDistributor::get_distributor( @@ -76,7 +78,8 @@ IcmpPacketDistributorItem IcmpPacketDistributor::get_distributor( const std::string &network_interface, const IoServiceItem io_serv ) { - DistributorInstanceIdentifier identifier(protocol, network_interface); + IcmpPacketDistributor::DistributorInstanceIdentifier identifier( + protocol, network_interface); // check if there is an instance for this protocol and interface if ( Instances.count(identifier) == 0 ) @@ -99,7 +102,8 @@ IcmpPacketDistributorItem IcmpPacketDistributor::get_distributor( const icmp::socket::protocol_type &protocol, const std::string &network_interface ) { - DistributorInstanceIdentifier identifier(protocol, network_interface); + IcmpPacketDistributor::DistributorInstanceIdentifier identifier( + protocol, network_interface); BOOST_ASSERT( Instances.count(identifier) == 1 ); @@ -186,7 +190,7 @@ void IcmpPacketDistributor::handle_receive( BOOST_FOREACH( IcmpPingerItem pinger, PingerList ) { packet_matches |= pinger->handle_receive_icmp_packet(icmp_packet, - bytes_received); + bytes_transferred); if (packet_matches) break; } @@ -194,7 +198,7 @@ void IcmpPacketDistributor::handle_receive( if (!packet_matches) GlobalLogger.warning() << "Packet did not match any pinger" << std::endl; - + } catch ( ... ) { GlobalLogger.notice() << "Exception during ICMP parse." << std::endl; @@ -204,7 +208,7 @@ void IcmpPacketDistributor::handle_receive( register_receive_handler(); } -bool IcmpPacketDistributor::register_pinger( const IcmpPingerItem new_pinger ) +bool IcmpPacketDistributor::register_pinger( const IcmpPingerItem &new_pinger ) { std::pair result = PingerList.insert(new_pinger); bool was_new = result.second; @@ -219,7 +223,7 @@ bool IcmpPacketDistributor::register_pinger( const IcmpPingerItem new_pinger ) } -bool IcmpPacketDistributor::unregister_pinger( const IcmpPingerItem old_pinger ) +bool IcmpPacketDistributor::unregister_pinger( const IcmpPingerItem &old_pinger ) { int n_erased = PingerList.erase(old_pinger); bool was_erased = n_erased > 0; @@ -238,7 +242,8 @@ bool IcmpPacketDistributor::unregister_pinger( const IcmpPingerItem old_pinger ) */ void IcmpPacketDistributor::clean_up_all() { - BOOST_FOREACH( map_type::value_type &instance, Instances ) + BOOST_FOREACH( IcmpPacketDistributor::map_type::value_type &instance, + Instances ) instance.second->clean_up(); Instances.clear(); diff --git a/src/icmp/icmppacketdistributor.h b/src/icmp/icmppacketdistributor.h index c8cfec7..9ea8c27 100644 --- a/src/icmp/icmppacketdistributor.h +++ b/src/icmp/icmppacketdistributor.h @@ -28,35 +28,20 @@ #include #include -#include "host/pinger.h" -#include "host/networkinterface.hpp" #include "icmp/icmppinger.h" using boost::asio::ip::icmp; -// for each IP protocol (v4/v6) and each network interface (string), -// there can only be one IcmpPacketDistributor instance -typedef std::pair - DistributorInstanceIdentifier; - -struct IcmpPacketDistributorInstanceIdentifierComparator -{ - bool operator() ( const DistributorInstanceIdentifier &a, - const DistributorInstanceIdentifier &b ) const ; -}; - -class IcmpPacketDistributor; - -typedef boost::shared_ptr IcmpPacketDistributorItem; -typedef std::map map_type; - +typedef boost::shared_ptr IoServiceItem; typedef boost::shared_ptr SocketItem; //----------------------------------------------------------------------------- // IcmpPacketDistributor //----------------------------------------------------------------------------- +class IcmpPacketDistributor; +typedef boost::shared_ptr IcmpPacketDistributorItem; + class IcmpPacketDistributor { public: @@ -108,12 +93,24 @@ private: std::set PingerList; + // for each IP protocol (v4/v6) and each network interface (string), + // there can only be one IcmpPacketDistributor instance + typedef std::pair + DistributorInstanceIdentifier; + + struct InstanceIdentifierComparator + { + bool operator() ( const DistributorInstanceIdentifier &a, + const DistributorInstanceIdentifier &b ) const ; + }; + + typedef std::map map_type; /// Instances, one for each (protocol, interface) - pair static map_type Instances; }; - #endif // ICMP_PACKET_DISTRIBUTOR_H // (created using vim -- the world's best text editor) diff --git a/src/icmp/icmppinger.cpp b/src/icmp/icmppinger.cpp index af2b8d7..d059240 100644 --- a/src/icmp/icmppinger.cpp +++ b/src/icmp/icmppinger.cpp @@ -15,11 +15,14 @@ #include #include #include +#include // DEBUG: distributor in pinger #include #include "boost_assert_handler.h" #include "icmp/icmppacketfactory.h" +//#include "icmp/icmppacketdistributor.h" // DEBUG: distributor in pinger +#include "host/networkinterface.hpp" // DEBUG: distributor in pinger using namespace std; using boost::asio::const_buffers_1; @@ -32,6 +35,8 @@ using boost::posix_time::seconds; using boost::shared_ptr; using I2n::Logger::GlobalLogger; +using boost::asio::ip::icmp; + //----------------------------------------------------------------------------- // IcmpPinger //----------------------------------------------------------------------------- @@ -52,7 +57,7 @@ PingerItem IcmpPinger::create( icmp::v4(), source_network_interface, io_serv); // create pinger - IcmpPinger *ptr = new IcmpPinger(io_serv, protocol, echo_reply_timeout_in_sec); + IcmpPinger *ptr = new IcmpPinger(io_serv, protocol, echo_reply_timeout_in_sec, distributor); IcmpPingerItem shared_ptr_(ptr); Pinger::WeakPtr weak_ptr( shared_ptr_ ); @@ -79,7 +84,8 @@ PingerItem IcmpPinger::create( IcmpPinger::IcmpPinger( const IoServiceItem io_serv, const icmp::socket::protocol_type &protocol, - const int echo_reply_timeout_in_sec + const int echo_reply_timeout_in_sec, + const IcmpPacketDistributorItem distributor ) : DestinationEndpoint(), Protocol( protocol ), @@ -90,7 +96,8 @@ IcmpPinger::IcmpPinger( ReplyReceived( false ), EchoReplyTimeoutInSec( echo_reply_timeout_in_sec ), PingerStatus( PingStatus_NotSent ), - PingDoneCallback() + PingDoneCallback(), + PacketDistributor( distributor ) { // Create "unique" identifier boost::uuids::random_generator random_gen; @@ -136,7 +143,19 @@ void IcmpPinger::ping( void IcmpPinger::stop_pinging() { - IcmpPingerItem icmp_item = boost::const_pointer_cast( get_myself().lock() ); + GlobalLogger.debug() + << DestinationEndpoint.address().to_string() + << ": stop_pinging" << endl; + + GlobalLogger.debug() + << DestinationEndpoint.address().to_string() + << ": cancel timer" << endl; + IcmpPacketReceiveTimer.cancel(); + IcmpPingerItem icmp_item = boost::static_pointer_cast( get_myself().lock() ); + + GlobalLogger.debug() + << DestinationEndpoint.address().to_string() + << ": cancel timer" << endl; PacketDistributor->unregister_pinger( icmp_item ); } @@ -315,7 +334,7 @@ bool IcmpPinger::handle_receive_icmp_packet(const IcmpPacketItem icmp_packet, { GlobalLogger.info() << DestinationEndpoint.address().to_string() - << ": Received packet that does not match" << endl; + << ": Received packet that does not match or is old" << endl; } return does_match; @@ -325,3 +344,258 @@ void IcmpPinger::set_ping_status( PingStatus ping_status ) { PingerStatus = ping_status; } + +//------------------------------------------------------------------------ +// IcmpPacketDistributor +//------------------------------------------------------------------------ + +static const std::size_t SOCKET_BUFFER_SIZE = 65536; // 64kB + +typedef std::set::iterator PingerListIterator; + + +bool IcmpPacketDistributor::InstanceIdentifierComparator::operator() ( + const IcmpPacketDistributor::DistributorInstanceIdentifier &a, + const IcmpPacketDistributor::DistributorInstanceIdentifier &b ) + const +{ + if ( a.first == boost::asio::ip::icmp::v4() ) + { + if ( b.first == boost::asio::ip::icmp::v4() ) + return a.second < b.second; // v4 == v4 + else + BOOST_ASSERT( b.first == boost::asio::ip::icmp::v6() ); + return true; // a(v4) < b(b6) + } + else + { + BOOST_ASSERT( a.first == boost::asio::ip::icmp::v6() ); + + if ( b.first == boost::asio::ip::icmp::v4() ) + return false; // a(v6) > b(v4) + else + BOOST_ASSERT( b.first == boost::asio::ip::icmp::v6() ); + return a.second < b.second; // v6 == v6 + } +} + +//----------------------------------------------------------------------------- +// Definition of IcmpPacketDistributor +//----------------------------------------------------------------------------- + +IcmpPacketDistributor::map_type IcmpPacketDistributor::Instances; // initialize + + +IcmpPacketDistributorItem IcmpPacketDistributor::get_distributor( + const icmp::socket::protocol_type &protocol, + const std::string &network_interface, + const IoServiceItem io_serv ) +{ + IcmpPacketDistributor::DistributorInstanceIdentifier identifier( + protocol, network_interface); + + // check if there is an instance for this protocol and interface + if ( Instances.count(identifier) == 0 ) + { // need to create an instance for this protocol and network interface + GlobalLogger.info() << "Creating IcmpPacketDistributor for interface " + << network_interface << std::endl; + IcmpPacketDistributorItem new_instance( new IcmpPacketDistributor( + protocol, network_interface, io_serv ) ); + Instances[identifier] = new_instance; + } + + BOOST_ASSERT( Instances.count(identifier) == 1 ); + + // return the one instance for this protocol and interface + return Instances[identifier]; +} + + +IcmpPacketDistributorItem IcmpPacketDistributor::get_distributor( + const icmp::socket::protocol_type &protocol, + const std::string &network_interface ) +{ + IcmpPacketDistributor::DistributorInstanceIdentifier identifier( + protocol, network_interface); + + BOOST_ASSERT( Instances.count(identifier) == 1 ); + + // return the one instance for this protocol and interface + return Instances[identifier]; +} + + +IcmpPacketDistributor::IcmpPacketDistributor( + const icmp::socket::protocol_type &protocol, + const std::string &network_interface, + const IoServiceItem io_serv ): + Protocol( protocol ), + ReplyBuffer(), + PingerList() +{ + Socket = SocketItem( new icmp::socket(*io_serv, protocol) ); + NetworkInterface + NetInterface( network_interface, *Socket ); + + if ( !NetInterface.bind() ) + { + GlobalLogger.error() + << "Trouble creating IcmpPacketDistributor for interface " + << network_interface// << " and protocol " << protocol + << ": could not bind the socket with the local interface. " + << ::strerror( errno ) << std::endl; + } + + register_receive_handler(); +} + + +void IcmpPacketDistributor::register_receive_handler() +{ + // wait for reply, prepare buffer to receive up to SOCKET_BUFFER_SIZE bytes + Socket->async_receive( + ReplyBuffer.prepare( SOCKET_BUFFER_SIZE ), + boost::bind( &IcmpPacketDistributor::handle_receive, this, + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred ) + ); +} + +void IcmpPacketDistributor::handle_receive( + const boost::system::error_code &error, + const size_t &bytes_transferred ) +{ + if ( error ) + { + GlobalLogger.warning() + << ": Received error " << error + << " in ICMP packet distributor; end handler and schedule another."; + register_receive_handler(); + return; + } + + // The actual number of bytes received is committed to the buffer so that we + // can extract it using a std::istream object. + ReplyBuffer.commit( bytes_transferred ); + + GlobalLogger.info() << "received packet in distributor" << std::endl; + + try + { + std::istream is( &ReplyBuffer ); + if ( !is ) + { + GlobalLogger.error() << "Can't handle ReplyBuffer" << std::endl; + return; + } + + // Decode the reply packet. + IcmpPacketItem icmp_packet = IcmpPacketFactory::create_icmp_packet( + Protocol, is ); + if ( !icmp_packet ) + { + GlobalLogger.warning() << "Ignoring broken ICMP packet" + << std::endl; + } + + // check which pinger wants this packet + //bool packet_matches = false; + int n_matches = 0; + BOOST_FOREACH( IcmpPingerItem pinger, PingerList ) + { + /* + packet_matches |= pinger->handle_receive_icmp_packet(icmp_packet, + bytes_transferred); + if (packet_matches) + break; + */ + if (pinger->handle_receive_icmp_packet(icmp_packet, + bytes_transferred) ) + ++n_matches; + } + BOOST_ASSERT(n_matches < 2); + + //if (!packet_matches) + if (n_matches == 0) + GlobalLogger.warning() << "Packet did not match any pinger" + << std::endl; + } + catch ( ... ) + { + GlobalLogger.notice() << "Exception during ICMP parse." << std::endl; + } + + // re-register receive handler + register_receive_handler(); +} + +bool IcmpPacketDistributor::register_pinger( const IcmpPingerItem &new_pinger ) +{ + std::pair result = PingerList.insert(new_pinger); + bool was_new = result.second; + if (was_new) + GlobalLogger.info() << "Register new pinger with IcmpPacketDistributor" + << std::endl; + else + GlobalLogger.warning() + << "Pinger to register was already known in IcmpPacketDistributor" + << std::endl; + return was_new; +} + + +bool IcmpPacketDistributor::unregister_pinger( const IcmpPingerItem &old_pinger ) +{ + int n_erased = PingerList.erase(old_pinger); + bool was_erased = n_erased > 0; + if (was_erased) + GlobalLogger.info() << "Removed pinger from IcmpPacketDistributor" + << std::endl; + else + GlobalLogger.warning() + << "Could not find pinger to remove from IcmpPacketDistributor" + << std::endl; + return was_erased; +} + +/** + * @brief for all instances: close sockets, unregister all pingers + */ +void IcmpPacketDistributor::clean_up_all() +{ + BOOST_FOREACH( IcmpPacketDistributor::map_type::value_type &instance, + Instances ) + instance.second->clean_up(); + + Instances.clear(); +} + +void IcmpPacketDistributor::clean_up() +{ + if (PingerList.size() > 0) + GlobalLogger.warning() << "There were still " << PingerList.size() + << " pingers registered in IcmpPacketDistributor!" << std::endl; + PingerList.clear(); + + boost::system::error_code error; + //Socket->shutdown(icmp::socket::shutdown_both, error); //both=send&receive + //if ( error ) + // GlobalLogger.warning() << "Received error " << error + // << " when shutting down ICMP socket"; + // always gave an error system:9 (probably EBADF: Bad file descriptor) + + Socket->close(error); + if ( error ) + GlobalLogger.warning() << "Received error " << error + << " when closing ICMP socket"; +} + +IcmpPacketDistributor::~IcmpPacketDistributor() +{ + GlobalLogger.info() << "Destroying IcmpPacketDistributor" << std::endl; +} + +SocketItem IcmpPacketDistributor::get_socket() const +{ + return Socket; +} diff --git a/src/icmp/icmppinger.h b/src/icmp/icmppinger.h index b6d0c9e..5f0a512 100644 --- a/src/icmp/icmppinger.h +++ b/src/icmp/icmppinger.h @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -19,11 +20,92 @@ #include "host/pinger.h" #include "host/pingstatus.h" #include "icmp/icmppacket.h" -#include "icmp/icmppacketdistributor.h" +//#include "icmp/icmppacketdistributor.h" DEBUG: distributor in pinger + + +using boost::asio::ip::icmp; class IcmpPinger; typedef boost::shared_ptr IcmpPingerItem; +class IcmpPacketDistributor; +typedef boost::shared_ptr IcmpPacketDistributorItem; + +typedef boost::shared_ptr IoServiceItem; +typedef boost::shared_ptr SocketItem; + +//----------------------------------------------------------------------------- +// IcmpPacketDistributor +//----------------------------------------------------------------------------- + + +class IcmpPacketDistributor +{ +public: + bool register_pinger( const IcmpPingerItem &new_pinger ); + bool unregister_pinger( const IcmpPingerItem &old_pinger ); + + SocketItem get_socket() const; + + static IcmpPacketDistributorItem get_distributor( + const icmp::socket::protocol_type &protocol, + const std::string &network_interface, + const IoServiceItem io_serv + ); + + static IcmpPacketDistributorItem get_distributor( + const icmp::socket::protocol_type &protocol, + const std::string &network_interface + ); + + static void clean_up_all(); + + ~IcmpPacketDistributor(); + +private: + // hide away constructor, copy constructor and assignment operator + IcmpPacketDistributor( + const icmp::socket::protocol_type &protocol, + const std::string &network_interface, + const IoServiceItem io_serv + ); + IcmpPacketDistributor(IcmpPacketDistributor const&); + void operator=(IcmpPacketDistributor const&); + + void register_receive_handler(); + void handle_receive( const boost::system::error_code &error, + const size_t &bytes_transferred ); + void clean_up(); + +private: + /// The socket object + SocketItem Socket; + + /// Network layer protocol used to ping, IPv4 or IPv6 + icmp::socket::protocol_type Protocol; + + /// The buffer where the data received will be placed + boost::asio::streambuf ReplyBuffer; + + std::set PingerList; + + // for each IP protocol (v4/v6) and each network interface (string), + // there can only be one IcmpPacketDistributor instance + typedef std::pair + DistributorInstanceIdentifier; + + struct InstanceIdentifierComparator + { + bool operator() ( const DistributorInstanceIdentifier &a, + const DistributorInstanceIdentifier &b ) const ; + }; + + typedef std::map map_type; + /// Instances, one for each (protocol, interface) - pair + static map_type Instances; +}; + //----------------------------------------------------------------------------- // IcmpPinger //----------------------------------------------------------------------------- @@ -52,11 +134,16 @@ public: virtual void stop_pinging(); + bool handle_receive_icmp_packet(const IcmpPacketItem icmp_packet, + const size_t bytes_transferred + ); + private: IcmpPinger( const IoServiceItem io_serv, const boost::asio::ip::icmp::socket::protocol_type &protocol, - const int echo_reply_timeout_in_sec + const int echo_reply_timeout_in_sec, + const IcmpPacketDistributorItem distributor ); void set_destination_endpoint( const std::string &destination_ip ); @@ -66,10 +153,6 @@ private: void schedule_timeout_echo_reply(); void handle_timeout(const boost::system::error_code &error); - bool handle_receive_icmp_packet(const IcmpPacketItem icmp_packet, - const size_t bytes_transferred - ); - void set_ping_status( PingStatus ping_status ); private: diff --git a/src/main.cpp b/src/main.cpp index 2cfdb90..e659e63 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -41,7 +41,8 @@ on this file might be covered by the GNU General Public License. #include "host/pingerfactory.h" #include "host/pingprotocol.h" #include "host/pingscheduler.h" -#include "icmp/icmppacketdistributor.h" +//#include "icmp/icmppacketdistributor.h" DEBUG: distributor in pinger +#include "icmp/icmppinger.h" // DEBUG: distributor in pinger using namespace std; @@ -52,7 +53,6 @@ using I2n::Logger::GlobalLogger; // a map from interval (in seconds) to delay (in seconds) typedef std::pair IntervalCountPair; typedef std::map DelayMap; -typedef shared_ptr IoServiceItem; typedef shared_ptr TimerItem; const boost::posix_time::time_duration SIGNAL_CHECK_INTERVAL = boost::posix_time::seconds(1); -- 1.7.1