From: Christian Herdtweck Date: Fri, 23 Jan 2015 17:07:37 +0000 (+0100) Subject: started to implement an icmp paket distributor; compiles and works at first, but... X-Git-Url: http://developer.intra2net.com/git/?a=commitdiff_plain;h=5a9bc2d137fad300b89a255591d7603197214cae;p=pingcheck started to implement an icmp paket distributor; compiles and works at first, but gives segmentation fault at exit --- diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a1d5cd6..608f250 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -88,6 +88,7 @@ set(SOURCES icmp/icmppacket.cpp icmp/icmppacketfactory.cpp icmp/icmppinger.cpp + icmp/icmppaketdistributor.cpp ip/ipv4header.cpp ip/ipv6header.cpp link/linkstatus.cpp diff --git a/src/host/pinger.h b/src/host/pinger.h index 7355637..b8bdd91 100644 --- a/src/host/pinger.h +++ b/src/host/pinger.h @@ -53,6 +53,8 @@ public: boost::function ping_done_callback ) = 0; + virtual void stop_pinging() = 0; + protected: Pinger(); virtual ~Pinger(); diff --git a/src/host/pingerfactory.cpp b/src/host/pingerfactory.cpp index 2ad9fbc..776a8b2 100644 --- a/src/host/pingerfactory.cpp +++ b/src/host/pingerfactory.cpp @@ -30,6 +30,7 @@ #include #include "boost_assert_handler.h" +#include "icmp/icmppaketdistributor.h" #include "icmp/icmppinger.h" #include "tcp/tcppinger.h" @@ -79,25 +80,38 @@ PingerItem PingerFactory::createPinger( try { - switch ( protocol ) + if (protocol == PingProtocol_ICMP) { - case PingProtocol_ICMP: - return PingerItem( + PingerItem new_pinger( new IcmpPinger( io_serv, icmp::v4(), network_interface, ping_reply_timeout ) ); - case PingProtocol_ICMPv6: - return PingerItem( + IcmpPaketDistributor::get_distributor(icmp::v4(), network_interface, io_serv) + ->register_pinger(new_pinger); + return new_pinger; + } + else if (protocol == PingProtocol_ICMPv6) + { + PingerItem new_pinger( new IcmpPinger( io_serv, icmp::v6(), network_interface, ping_reply_timeout ) ); - case PingProtocol_TCP: + IcmpPaketDistributor::get_distributor(icmp::v6(), network_interface, io_serv) + ->register_pinger(new_pinger); + return new_pinger; + } + else if (protocol == PingProtocol_TCP) + { return PingerItem( new TcpPinger( io_serv, tcp_raw_protocol::v4(), network_interface, ping_reply_timeout ) ); - case PingProtocol_TCP_IPv6: + } + else if (protocol == PingProtocol_TCP_IPv6) + { return PingerItem( new TcpPinger( io_serv, tcp_raw_protocol::v6(), network_interface, ping_reply_timeout ) ); - default: + } + else + { BOOST_ASSERT( !"Try to create a pinger from an invalid protocol" ); //lint !e506 return PingerItem(); //lint !e527 } diff --git a/src/host/pingrotate.cpp b/src/host/pingrotate.cpp index 641b58f..20dd11e 100644 --- a/src/host/pingrotate.cpp +++ b/src/host/pingrotate.cpp @@ -108,6 +108,11 @@ void PingRotate::ping( function ping_done_callback ) ); } +void PingRotate::stop_pinging() +{ + Ping->stop_pinging(); +} + bool PingRotate::resolve_ping_address() //lint !e1762 { return IpList->resolve(); diff --git a/src/host/pingrotate.h b/src/host/pingrotate.h index e287e16..e6e5367 100644 --- a/src/host/pingrotate.h +++ b/src/host/pingrotate.h @@ -59,6 +59,8 @@ public: void ping( boost::function ping_done_callback ); + void stop_pinging(); + bool resolve_ping_address(); int get_resolved_ip_count() const; bool expired_resolved_ip() const; diff --git a/src/host/pingscheduler.cpp b/src/host/pingscheduler.cpp index 08f8c3d..c737a46 100644 --- a/src/host/pingscheduler.cpp +++ b/src/host/pingscheduler.cpp @@ -116,6 +116,7 @@ PingScheduler::~PingScheduler() void PingScheduler::stop_pinging() { + Ping->stop_pinging(); } /** diff --git a/src/icmp/icmppaketdistributor.cpp b/src/icmp/icmppaketdistributor.cpp new file mode 100644 index 0000000..9910b5c --- /dev/null +++ b/src/icmp/icmppaketdistributor.cpp @@ -0,0 +1,191 @@ +/* + The software in this package is distributed under the GNU General + Public License version 2 (with a special exception described below). + + A copy of GNU General Public License (GPL) is included in this distribution, + in the file COPYING.GPL. + + As a special exception, if other files instantiate templates or use macros + or inline functions from this file, or you compile this file and link it + with other works to produce a work based on this file, this file + does not by itself cause the resulting work to be covered + by the GNU General Public License. + + However the source code for this file must still be made available + in accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work based + on this file might be covered by the GNU General Public License. + + Christian Herdtweck, Intra2net AG 2015 + */ + +#include "icmppaketdistributor.h" + +#include +#include +#include + +#include "boost_assert_handler.h" + + +using I2n::Logger::GlobalLogger; +using boost::asio::ip::icmp; + +static const std::size_t SOCKET_BUFFER_SIZE = 65536; // 64kB + +typedef std::set::iterator PingerListIterator; + + +bool IcmpPaketDistributorInstanceIdentifierComparator::operator() ( + const DistributorInstanceIdentifier &a, + const 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 IcmpPaketDistributor +//----------------------------------------------------------------------------- + +std::map IcmpPaketDistributor::Instances; + +IcmpPaketDistributorItem IcmpPaketDistributor::get_distributor( + const icmp::socket::protocol_type &protocol, + const std::string &network_interface, + const IoServiceItem io_serv ) +{ + 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 IcmpPaketDistributor for interface " + << network_interface << std::endl; + IcmpPaketDistributorItem new_instance( new IcmpPaketDistributor( + 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]; +} + + +IcmpPaketDistributorItem IcmpPaketDistributor::get_distributor( + const icmp::socket::protocol_type &protocol, + const std::string &network_interface ) +{ + DistributorInstanceIdentifier identifier(protocol, network_interface); + + BOOST_ASSERT( Instances.count(identifier) == 1 ); + + // return the one instance for this protocol and interface + return Instances[identifier]; +} + + +IcmpPaketDistributor::IcmpPaketDistributor( + const icmp::socket::protocol_type &protocol, + const std::string &network_interface, + const IoServiceItem io_serv ): + Socket( *io_serv, protocol ), + Protocol( protocol ), + ReplyBuffer(), + PingerList() +{ + NetworkInterface + NetInterface( network_interface, Socket ); + + if ( !NetInterface.bind() ) + { + GlobalLogger.error() + << "Trouble creating IcmpPaketDistributor for interface " + << network_interface// << " and protocol " << protocol + << ": could not bind the socket with the local interface. " + << ::strerror( errno ) << std::endl; + } + + register_receive_handler(); +} + + +void IcmpPaketDistributor::register_receive_handler() +{ + // Waiting for a reply, We prepare the buffer to receive up to SOCKET_BUFFER_SIZE bytes + Socket.async_receive( + ReplyBuffer.prepare( SOCKET_BUFFER_SIZE ), + boost::bind( &IcmpPaketDistributor::handle_receive, this, + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred ) + ); +} + +void IcmpPaketDistributor::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 paket in distributor" << std::endl; + + register_receive_handler(); +} + +bool IcmpPaketDistributor::register_pinger( const PingerItem new_pinger ) +{ + std::pair result = PingerList.insert(new_pinger); + bool was_new = result.second; + if (was_new) + GlobalLogger.info() << "Register new pinger with IcmpPaketDistributor" << std::endl; + else + GlobalLogger.warning() << "Pinger to register was already known in IcmpPaketDistributor" + << std::endl; + return was_new; +} + + +bool IcmpPaketDistributor::unregister_pinger( const PingerItem old_pinger ) +{ + int n_erased = PingerList.erase(old_pinger); + bool was_erased = n_erased > 0; + if (was_erased) + GlobalLogger.info() << "Removed pinger from IcmpPaketDistributor" << std::endl; + else + GlobalLogger.warning() << "Could not find pinger to remove from IcmpPaketDistributor" + << std::endl; + return was_erased; +} + +// (created using vim -- the world's best text editor) + diff --git a/src/icmp/icmppaketdistributor.h b/src/icmp/icmppaketdistributor.h new file mode 100644 index 0000000..29310ba --- /dev/null +++ b/src/icmp/icmppaketdistributor.h @@ -0,0 +1,107 @@ +/* + The software in this package is distributed under the GNU General + Public License version 2 (with a special exception described below). + + A copy of GNU General Public License (GPL) is included in this distribution, + in the file COPYING.GPL. + + As a special exception, if other files instantiate templates or use macros + or inline functions from this file, or you compile this file and link it + with other works to produce a work based on this file, this file + does not by itself cause the resulting work to be covered + by the GNU General Public License. + + However the source code for this file must still be made available + in accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work based + on this file might be covered by the GNU General Public License. + + Christian Herdtweck, Intra2net AG 2015 + */ + +#ifndef ICMP_PAKET_DISTRIBUTOR_H +#define ICMP_PAKET_DISTRIBUTOR_H + +#include +#include +#include +#include + +#include "host/pinger.h" +#include "host/networkinterface.hpp" + +using boost::asio::ip::icmp; + +// for each IP protocol (v4/v6) and each network interface (string), +// there can only be one IcmpPaketDistributor instance +typedef std::pair DistributorInstanceIdentifier; + +struct IcmpPaketDistributorInstanceIdentifierComparator +{ + bool operator() ( const DistributorInstanceIdentifier &a, + const DistributorInstanceIdentifier &b ) const ; +}; + +class IcmpPaketDistributor; + +typedef boost::shared_ptr IcmpPaketDistributorItem; + +//----------------------------------------------------------------------------- +// IcmpPaketDistributor +//----------------------------------------------------------------------------- + +class IcmpPaketDistributor +{ +public: + bool register_pinger( const PingerItem new_pinger ); + bool unregister_pinger( const PingerItem old_pinger ); + + static IcmpPaketDistributorItem get_distributor( + const icmp::socket::protocol_type &protocol, + const std::string &network_interface, + const IoServiceItem io_serv ); + + static IcmpPaketDistributorItem get_distributor( + const icmp::socket::protocol_type &protocol, + const std::string &network_interface ); + + ~IcmpPaketDistributor() {}; + +private: + // hide away constructor, copy constructor and copy operator + IcmpPaketDistributor( + const icmp::socket::protocol_type &protocol, + const std::string &network_interface, + const IoServiceItem io_serv ); + IcmpPaketDistributor(IcmpPaketDistributor const&); + void operator=(IcmpPaketDistributor const&); + + void register_receive_handler(); + void handle_receive( const boost::system::error_code &error, + const size_t &bytes_transferred ); + + +private: + /// The socket object + icmp::socket 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; + + /// Instances, one for each (protocol, interface) - pair + static std::map Instances; + +}; + + +#endif // ICMP_PAKET_DISTRIBUTOR_H + +// (created using vim -- the world's best text editor) + diff --git a/src/icmp/icmppinger.cpp b/src/icmp/icmppinger.cpp index f0079e0..92b6560 100644 --- a/src/icmp/icmppinger.cpp +++ b/src/icmp/icmppinger.cpp @@ -22,6 +22,7 @@ #include "boost_assert_handler.h" #include "icmp/icmppacketfactory.h" +#include "icmp/icmppaketdistributor.h" using namespace std; using boost::asio::const_buffers_1; @@ -127,6 +128,13 @@ void IcmpPinger::ping( // there might still be an old handler in place... cancel? } +void IcmpPinger::stop_pinging() +{ + IcmpPaketDistributor::get_distributor(Protocol, NetInterface.get_name()) + ->unregister_pinger(PingerItem(this)); +} + + void IcmpPinger::set_destination_endpoint( const string &destination_ip ) { BOOST_ASSERT( !destination_ip.empty() ); diff --git a/src/icmp/icmppinger.h b/src/icmp/icmppinger.h index b9fb752..61cab02 100644 --- a/src/icmp/icmppinger.h +++ b/src/icmp/icmppinger.h @@ -45,6 +45,8 @@ public: boost::function ping_done_callback ); + virtual void stop_pinging(); + private: void set_destination_endpoint( const std::string &destination_ip ); diff --git a/src/tcp/tcppinger.cpp b/src/tcp/tcppinger.cpp index 028573e..1b43a62 100644 --- a/src/tcp/tcppinger.cpp +++ b/src/tcp/tcppinger.cpp @@ -132,6 +132,10 @@ void TcpPinger::ping( start_receive(); } +void TcpPinger::stop_pinging() +{ +} + address TcpPinger::get_source_address() const { return NetInterface.get_address( Protocol ); diff --git a/src/tcp/tcppinger.h b/src/tcp/tcppinger.h index 690fe29..d579cd9 100644 --- a/src/tcp/tcppinger.h +++ b/src/tcp/tcppinger.h @@ -58,6 +58,8 @@ public: boost::function ping_done_callback ); + virtual void stop_pinging(); + private: boost::asio::ip::address get_source_address() const; boost::asio::ip::address get_destination_address() const;