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
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();
}
/**
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
//
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);
#include <boost/foreach.hpp>
#include "boost_assert_handler.h"
+#include "host/networkinterface.hpp"
#include "icmp/icmppacketfactory.h"
typedef std::set<IcmpPingerItem>::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() )
{
// Definition of IcmpPacketDistributor
//-----------------------------------------------------------------------------
-map_type IcmpPacketDistributor::Instances; // initialize
+IcmpPacketDistributor::map_type IcmpPacketDistributor::Instances; // initialize
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 )
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 );
BOOST_FOREACH( IcmpPingerItem pinger, PingerList )
{
packet_matches |= pinger->handle_receive_icmp_packet(icmp_packet,
- bytes_received);
+ bytes_transferred);
if (packet_matches)
break;
}
if (!packet_matches)
GlobalLogger.warning() << "Packet did not match any pinger"
<< std::endl;
-
+ }
catch ( ... )
{
GlobalLogger.notice() << "Exception during ICMP parse." << std::endl;
register_receive_handler();
}
-bool IcmpPacketDistributor::register_pinger( const IcmpPingerItem new_pinger )
+bool IcmpPacketDistributor::register_pinger( const IcmpPingerItem &new_pinger )
{
std::pair<PingerListIterator, bool> result = PingerList.insert(new_pinger);
bool was_new = result.second;
}
-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;
*/
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();
#include <boost/shared_ptr.hpp>
#include <boost/asio.hpp>
-#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<icmp::socket::protocol_type, std::string>
- DistributorInstanceIdentifier;
-
-struct IcmpPacketDistributorInstanceIdentifierComparator
-{
- bool operator() ( const DistributorInstanceIdentifier &a,
- const DistributorInstanceIdentifier &b ) const ;
-};
-
-class IcmpPacketDistributor;
-
-typedef boost::shared_ptr<IcmpPacketDistributor> IcmpPacketDistributorItem;
-typedef std::map<DistributorInstanceIdentifier, IcmpPacketDistributorItem,
- IcmpPacketDistributorInstanceIdentifierComparator> map_type;
-
+typedef boost::shared_ptr<boost::asio::io_service> IoServiceItem;
typedef boost::shared_ptr<icmp::socket> SocketItem;
//-----------------------------------------------------------------------------
// IcmpPacketDistributor
//-----------------------------------------------------------------------------
+class IcmpPacketDistributor;
+typedef boost::shared_ptr<IcmpPacketDistributor> IcmpPacketDistributorItem;
+
class IcmpPacketDistributor
{
public:
std::set<IcmpPingerItem> PingerList;
+ // for each IP protocol (v4/v6) and each network interface (string),
+ // there can only be one IcmpPacketDistributor instance
+ typedef std::pair<icmp::socket::protocol_type, std::string>
+ DistributorInstanceIdentifier;
+
+ struct InstanceIdentifierComparator
+ {
+ bool operator() ( const DistributorInstanceIdentifier &a,
+ const DistributorInstanceIdentifier &b ) const ;
+ };
+
+ typedef std::map<DistributorInstanceIdentifier, IcmpPacketDistributorItem,
+ InstanceIdentifierComparator> 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)
#include <boost/date_time/posix_time/posix_time_types.hpp>
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_generators.hpp>
+#include <boost/foreach.hpp> // DEBUG: distributor in pinger
#include <logfunc.hpp>
#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;
using boost::shared_ptr;
using I2n::Logger::GlobalLogger;
+using boost::asio::ip::icmp;
+
//-----------------------------------------------------------------------------
// IcmpPinger
//-----------------------------------------------------------------------------
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_ );
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 ),
ReplyReceived( false ),
EchoReplyTimeoutInSec( echo_reply_timeout_in_sec ),
PingerStatus( PingStatus_NotSent ),
- PingDoneCallback()
+ PingDoneCallback(),
+ PacketDistributor( distributor )
{
// Create "unique" identifier
boost::uuids::random_generator random_gen;
void IcmpPinger::stop_pinging()
{
- IcmpPingerItem icmp_item = boost::const_pointer_cast<IcmpPinger>( 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<IcmpPinger>( get_myself().lock() );
+
+ GlobalLogger.debug()
+ << DestinationEndpoint.address().to_string()
+ << ": cancel timer" << endl;
PacketDistributor->unregister_pinger( icmp_item );
}
{
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;
{
PingerStatus = ping_status;
}
+
+//------------------------------------------------------------------------
+// IcmpPacketDistributor
+//------------------------------------------------------------------------
+
+static const std::size_t SOCKET_BUFFER_SIZE = 65536; // 64kB
+
+typedef std::set<IcmpPingerItem>::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<icmp::socket, boost::asio::ip::icmp>
+ 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<PingerListIterator, bool> 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;
+}
#include <stdint.h>
#include <string>
+#include <set>
#include <boost/asio.hpp>
#include <boost/function.hpp>
#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<IcmpPinger> IcmpPingerItem;
+class IcmpPacketDistributor;
+typedef boost::shared_ptr<IcmpPacketDistributor> IcmpPacketDistributorItem;
+
+typedef boost::shared_ptr<boost::asio::io_service> IoServiceItem;
+typedef boost::shared_ptr<icmp::socket> 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<IcmpPingerItem> PingerList;
+
+ // for each IP protocol (v4/v6) and each network interface (string),
+ // there can only be one IcmpPacketDistributor instance
+ typedef std::pair<icmp::socket::protocol_type, std::string>
+ DistributorInstanceIdentifier;
+
+ struct InstanceIdentifierComparator
+ {
+ bool operator() ( const DistributorInstanceIdentifier &a,
+ const DistributorInstanceIdentifier &b ) const ;
+ };
+
+ typedef std::map<DistributorInstanceIdentifier, IcmpPacketDistributorItem,
+ InstanceIdentifierComparator> map_type;
+ /// Instances, one for each (protocol, interface) - pair
+ static map_type Instances;
+};
+
//-----------------------------------------------------------------------------
// IcmpPinger
//-----------------------------------------------------------------------------
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 );
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:
#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;
// a map from interval (in seconds) to delay (in seconds)
typedef std::pair<int, float> IntervalCountPair;
typedef std::map<int, float> DelayMap;
-typedef shared_ptr<boost::asio::io_service> IoServiceItem;
typedef shared_ptr<boost::asio::deadline_timer> TimerItem;
const boost::posix_time::time_duration SIGNAL_CHECK_INTERVAL = boost::posix_time::seconds(1);