started to implement an icmp paket distributor; compiles and works at first, but...
authorChristian Herdtweck <christian.herdtweck@intra2net.com>
Fri, 23 Jan 2015 17:07:37 +0000 (18:07 +0100)
committerChristian Herdtweck <christian.herdtweck@intra2net.com>
Fri, 23 Jan 2015 17:07:37 +0000 (18:07 +0100)
12 files changed:
src/CMakeLists.txt
src/host/pinger.h
src/host/pingerfactory.cpp
src/host/pingrotate.cpp
src/host/pingrotate.h
src/host/pingscheduler.cpp
src/icmp/icmppaketdistributor.cpp [new file with mode: 0644]
src/icmp/icmppaketdistributor.h [new file with mode: 0644]
src/icmp/icmppinger.cpp
src/icmp/icmppinger.h
src/tcp/tcppinger.cpp
src/tcp/tcppinger.h

index a1d5cd6..608f250 100644 (file)
@@ -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
index 7355637..b8bdd91 100644 (file)
@@ -53,6 +53,8 @@ public:
             boost::function<void(bool)> ping_done_callback
     ) = 0;
 
+    virtual void stop_pinging() = 0;
+
 protected:
     Pinger();
     virtual ~Pinger();
index 2ad9fbc..776a8b2 100644 (file)
@@ -30,6 +30,7 @@
 #include <logfunc.hpp>
 
 #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
         }
index 641b58f..20dd11e 100644 (file)
@@ -108,6 +108,11 @@ void PingRotate::ping( function<void(bool)> ping_done_callback )
     );
 }
 
+void PingRotate::stop_pinging()
+{
+    Ping->stop_pinging();
+}
+
 bool PingRotate::resolve_ping_address()                                                         //lint !e1762
 {
     return IpList->resolve();
index e287e16..e6e5367 100644 (file)
@@ -59,6 +59,8 @@ public:
 
     void ping( boost::function<void(bool)> ping_done_callback );
 
+    void stop_pinging();
+
     bool resolve_ping_address();
     int get_resolved_ip_count() const;
     bool expired_resolved_ip() const;
index 08f8c3d..c737a46 100644 (file)
@@ -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 (file)
index 0000000..9910b5c
--- /dev/null
@@ -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 <errno.h>
+#include <logfunc.hpp>
+#include <boost/bind.hpp>
+
+#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<PingerItem>::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<DistributorInstanceIdentifier, IcmpPaketDistributorItem,
+         IcmpPaketDistributorInstanceIdentifierComparator> 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<icmp::socket, boost::asio::ip::icmp>
+                  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<PingerListIterator, bool> 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 (file)
index 0000000..29310ba
--- /dev/null
@@ -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 <set>
+#include <map>
+#include <boost/shared_ptr.hpp>
+#include <boost/asio.hpp>
+
+#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<icmp::socket::protocol_type, std::string> DistributorInstanceIdentifier;
+
+struct IcmpPaketDistributorInstanceIdentifierComparator
+{
+    bool operator() ( const DistributorInstanceIdentifier &a,
+                      const DistributorInstanceIdentifier &b ) const ;
+};
+
+class IcmpPaketDistributor;
+
+typedef boost::shared_ptr<IcmpPaketDistributor> 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<PingerItem> PingerList;
+
+    /// Instances, one for each (protocol, interface) - pair
+    static std::map<DistributorInstanceIdentifier, IcmpPaketDistributorItem,
+                    IcmpPaketDistributorInstanceIdentifierComparator> Instances;
+
+};
+
+
+#endif  // ICMP_PAKET_DISTRIBUTOR_H
+
+// (created using vim -- the world's best text editor)
+
index f0079e0..92b6560 100644 (file)
@@ -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() );
index b9fb752..61cab02 100644 (file)
@@ -45,6 +45,8 @@ public:
             boost::function<void(bool)> ping_done_callback
     );
 
+    virtual void stop_pinging();
+
 private:
     void set_destination_endpoint( const std::string &destination_ip );
 
index 028573e..1b43a62 100644 (file)
@@ -132,6 +132,10 @@ void TcpPinger::ping(
     start_receive();
 }
 
+void TcpPinger::stop_pinging()
+{
+}
+
 address TcpPinger::get_source_address() const
 {
     return NetInterface.get_address( Protocol );
index 690fe29..d579cd9 100644 (file)
@@ -58,6 +58,8 @@ public:
             boost::function<void(bool)> ping_done_callback
     );
 
+    virtual void stop_pinging();
+
 private:
     boost::asio::ip::address get_source_address() const;
     boost::asio::ip::address get_destination_address() const;