From 822e499169afefeff5bef90d1521dba951f19125 Mon Sep 17 00:00:00 2001 From: Guilherme Maciel Ferreira Date: Thu, 7 Apr 2011 12:54:37 +0200 Subject: [PATCH] Implemented the requirement "Support selection of the source network interface, so we can change the source ip address when ping" - unfortunately, I couldn't find a way to bind to an interface through Bosst Asio. So it was required to rely on native Unix Socket interface. --- src/main.cpp | 3 ++ src/ping/boostpinger.cpp | 62 ++++++++++++++++++++++++++++++++++++++----- src/ping/boostpinger.h | 5 +++ src/ping/pingscheduler.cpp | 8 +++++- src/ping/pingscheduler.h | 2 + 5 files changed, 71 insertions(+), 9 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index f7e8042..6833ad2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -39,6 +39,8 @@ int main( int argc, char* argv[] ) ) ); + string ping_interface = config.get_source_network_interface(); + // TODO init_pingers() vector< HostItem > hosts = config.get_hosts(); vector< PingSchedulerItem > scheduler_list; @@ -49,6 +51,7 @@ int main( int argc, char* argv[] ) PingSchedulerItem scheduler( new PingScheduler( io_serv, + ping_interface, ping_address, ping_interval_in_sec, ping_fail_limit, diff --git a/src/ping/boostpinger.cpp b/src/ping/boostpinger.cpp index 406ad66..26b4f4c 100644 --- a/src/ping/boostpinger.cpp +++ b/src/ping/boostpinger.cpp @@ -22,7 +22,8 @@ using namespace boost::posix_time; //----------------------------------------------------------------------------- BoostPinger::BoostPinger( - boost::asio::io_service &io_serv, + io_service &io_serv, + string source_network_interface, const int echo_reply_timeout_in_sec ) : IoService( io_serv ), @@ -36,6 +37,14 @@ BoostPinger::BoostPinger( EchoReplyTimeoutInSec( echo_reply_timeout_in_sec ), PingerStatus( PingStatus_NotSent ) { + BOOST_ASSERT( !source_network_interface.empty() ); + + if ( !select_source_network_interface( source_network_interface ) ) + { + cerr << "Error: could not bind the socket with the local interface." + << ::strerror( errno ) << endl; + } + } BoostPinger::~BoostPinger() @@ -125,15 +134,22 @@ void BoostPinger::send_echo_request( const IcmpPacket &icmp_packet ) TimeSent = microsec_clock::universal_time(); - // Send the request. string dest_address_string = DestinationEndpoint.address().to_string(); BOOST_ASSERT( !dest_address_string.empty() ); - const_buffers_1 data = request_buffer.data(); - size_t bytes_sent = Socket.send_to( data, DestinationEndpoint ); - if ( bytes_sent != buffer_size( data ) ) + // Send the request + try + { + const_buffers_1 data = request_buffer.data(); + size_t bytes_sent = Socket.send_to( data, DestinationEndpoint ); + if ( bytes_sent != buffer_size( data ) ) + { + cerr << "Error: fail sending ping data." << endl; + } + } + catch ( const exception &ex ) { - cerr << "Error: fail sending ping data." << endl; + cerr << "Error: fail sending ping data. " << ex.what() << endl; } schedule_timeout_echo_reply(); @@ -222,14 +238,14 @@ void BoostPinger::print_echo_reply( IcmpHeader icmp_hdr = icmp_packet.get_icmp_header(); size_t bytes_received = bytes_transferred - ipv4_hdr.get_header_length(); - string source_address = ipv4_hdr.get_source_address().to_string(); + string remote_address = ipv4_hdr.get_source_address().to_string(); int sequence_number = icmp_hdr.get_sequence_number(); int ttl = ipv4_hdr.get_time_to_live(); ptime now = microsec_clock::universal_time(); int time = (now - TimeSent).total_milliseconds(); cout << bytes_received << " bytes " - << "from " << source_address + << "from " << remote_address << ": icmp_seq=" << sequence_number << " ttl=" << ttl << " time=" << time << " ms" << endl; @@ -244,3 +260,33 @@ uint16_t BoostPinger::get_identifier() const { return static_cast ( ::getpid() ); } + +/** + * Avoid the socket to drop to another network interface if the destination + * is unreachable through the binded interface. Packets are sent only from + * this interface. + * + * @param source_network_interface The network interface to bind the pinger. + * + * @return false if the bind failed. + */ +bool BoostPinger::select_source_network_interface( + const string &source_network_interface +) +{ + BOOST_ASSERT( !source_network_interface.empty() ); + + int ret = ::setsockopt( + Socket.native(), + SOL_SOCKET, + SO_BINDTODEVICE, + source_network_interface.c_str(), + source_network_interface.size() + ); + if ( ret == -1 ) + { + return false; + } + + return true; +} diff --git a/src/ping/boostpinger.h b/src/ping/boostpinger.h index 1ce974f..766378c 100644 --- a/src/ping/boostpinger.h +++ b/src/ping/boostpinger.h @@ -18,6 +18,7 @@ class BoostPinger public: BoostPinger( boost::asio::io_service &io_serv, + std::string source_network_interface, const int echo_reply_timeout_in_sec ); virtual ~BoostPinger(); @@ -54,6 +55,10 @@ private: uint16_t get_identifier() const; + bool select_source_network_interface( + const std::string &source_network_interface + ); + private: boost::asio::io_service &IoService; boost::asio::ip::icmp::endpoint DestinationEndpoint; diff --git a/src/ping/pingscheduler.cpp b/src/ping/pingscheduler.cpp index 47f68cf..9f1a32a 100644 --- a/src/ping/pingscheduler.cpp +++ b/src/ping/pingscheduler.cpp @@ -19,6 +19,7 @@ using namespace boost::posix_time; PingScheduler::PingScheduler( boost::asio::io_service &io_serv, + const string &ping_interface, const string &ping_address, const long ping_interval_in_sec, const int ping_fail_percentage_limit, @@ -26,6 +27,7 @@ PingScheduler::PingScheduler( ) : IoService( io_serv ), + LocalNetworkInterfaceName( ping_interface ), Timer( io_serv ), TimeSentLastPing( microsec_clock::universal_time() ), PingIntervalInSec( ping_interval_in_sec ), @@ -84,8 +86,12 @@ bool PingScheduler::ping( const string &destination_ip ) const io_service io_serv; int echo_reply_timeout_in_sec = 5; // TODO configurable: this is the timeout to WAIT FOR the ping before considering a timeout - BoostPinger pinger( io_serv, echo_reply_timeout_in_sec ); + BoostPinger pinger( + io_serv, + LocalNetworkInterfaceName, + echo_reply_timeout_in_sec + ); return pinger.ping( destination_ip ); } diff --git a/src/ping/pingscheduler.h b/src/ping/pingscheduler.h index ef0b14f..0ace594 100644 --- a/src/ping/pingscheduler.h +++ b/src/ping/pingscheduler.h @@ -25,6 +25,7 @@ class PingScheduler public: PingScheduler( boost::asio::io_service &io_serv, + const std::string &ping_interface, const std::string &ping_address, const long ping_interval_in_sec, const int ping_fail_percentage_limit, @@ -46,6 +47,7 @@ private: private: boost::asio::io_service &IoService; + std::string LocalNetworkInterfaceName; boost::asio::deadline_timer Timer; boost::posix_time::ptime TimeSentLastPing; PingInterval PingIntervalInSec; -- 1.7.1