Implemented the requirement "Support selection of the source network interface, so...
authorGuilherme Maciel Ferreira <guilherme.maciel.ferreira@intra2net.com>
Thu, 7 Apr 2011 10:54:37 +0000 (12:54 +0200)
committerGuilherme Maciel Ferreira <guilherme.maciel.ferreira@intra2net.com>
Thu, 7 Apr 2011 10:54:37 +0000 (12:54 +0200)
- 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
src/ping/boostpinger.cpp
src/ping/boostpinger.h
src/ping/pingscheduler.cpp
src/ping/pingscheduler.h

index f7e8042..6833ad2 100644 (file)
@@ -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,
index 406ad66..26b4f4c 100644 (file)
@@ -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<uint16_t> ( ::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;
+}
index 1ce974f..766378c 100644 (file)
@@ -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;
index 47f68cf..9f1a32a 100644 (file)
@@ -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 );
 }
 
index ef0b14f..0ace594 100644 (file)
@@ -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<long> PingIntervalInSec;