Retrieve the local IP address from the interface using the low level ioctl, plus...
authorGuilherme Maciel Ferreira <guilherme.maciel.ferreira@gmail.com>
Mon, 1 Aug 2011 00:12:48 +0000 (21:12 -0300)
committerGuilherme Maciel Ferreira <guilherme.maciel.ferreira@gmail.com>
Mon, 1 Aug 2011 00:12:48 +0000 (21:12 -0300)
src/tcp/tcppinger.cpp
src/tcp/tcppinger.h

index 8f4cd54..3b9b72c 100644 (file)
@@ -19,6 +19,11 @@ on this file might be covered by the GNU General Public License.
 */
 #include "tcp/tcppinger.h"
 
+#include <errno.h>
+#include <net/if.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
 #include <istream>
 #include <ostream>
 
@@ -49,12 +54,13 @@ using I2n::Logger::GlobalLogger;
 
 TcpPinger::TcpPinger(
         io_service &io_serv,
-        const string &source_network_interface,
+        const string &source_network_interface_name,
         const int echo_reply_timeout_in_sec
 ) :
     IoService( io_serv ),
     DestinationEndpoint(),
     Socket( IoService, tcp_raw_protocol::v4() ),
+    SourceNetworkInterfaceName( source_network_interface_name ),
     TcpSegmentReceiveTimer( IoService ),
     Identifier( 0 ),
     SequenceNumber( 0 ),
@@ -65,11 +71,11 @@ TcpPinger::TcpPinger(
     PingerStatus( PingStatus_NotSent ),
     PingDoneCallback()
 {
-    if ( !source_network_interface.empty() &&
-         !select_source_network_interface( source_network_interface ) )
+    if ( !source_network_interface_name.empty() &&
+         !select_source_network_interface( source_network_interface_name ) )
     {
         GlobalLogger.error() << "Error: could not bind the socket "
-                "with the local interface." << ::strerror( errno )  << endl;
+                "with the local interface. " << ::strerror( errno )  << endl;
     }
 
     // Create "unique" identifier
@@ -108,17 +114,30 @@ void TcpPinger::ping(
     start_receive();
 }
 
-uint32_t TcpPinger::get_source_address() const
+uint32_t TcpPinger::get_source_address()
 {
-    return 0xC0A80166; // TODO 192.168.1.102
+    struct ifreq ifr;
+    memset( &ifr, 0, sizeof(ifr) );
+    strcpy( ifr.ifr_name, SourceNetworkInterfaceName.c_str() );
+    ifr.ifr_addr.sa_family = AF_INET;
+    int ioctl_resp = ioctl( Socket.native(), SIOCGIFADDR, &ifr );
+    if ( ioctl_resp == 0)
+    {
+        return ((uint32_t) ifr.ifr_addr.sa_data[2] & 0xFF) << 24 |
+               ((uint32_t) ifr.ifr_addr.sa_data[3] & 0xFF) << 16 |
+               ((uint32_t) ifr.ifr_addr.sa_data[4] & 0xFF) << 8 |
+               ((uint32_t) ifr.ifr_addr.sa_data[5] & 0xFF);
+    }
+
+    return 0;
 }
 
 uint32_t TcpPinger::get_destination_address() const
 {
-    return ((unsigned int) DestinationEndpoint.data()->sa_data[2] & 0xFF) << 24 |
-           ((unsigned int) DestinationEndpoint.data()->sa_data[3] & 0xFF) << 16 |
-           ((unsigned int) DestinationEndpoint.data()->sa_data[4] & 0xFF) << 8 |
-           ((unsigned int) DestinationEndpoint.data()->sa_data[5] & 0xFF);
+    return ((uint32_t) DestinationEndpoint.data()->sa_data[2] & 0xFF) << 24 |
+           ((uint32_t) DestinationEndpoint.data()->sa_data[3] & 0xFF) << 16 |
+           ((uint32_t) DestinationEndpoint.data()->sa_data[4] & 0xFF) << 8 |
+           ((uint32_t) DestinationEndpoint.data()->sa_data[5] & 0xFF);
 }
 
 void TcpPinger::set_destination_endpoint( const string &destination_ip )
@@ -143,11 +162,12 @@ void TcpPinger::start_send()
             ++SequenceNumber
     );
 
-    // TODO isolate this part, so it can change without break the rest of the code
-    uint32_t src_addr = get_source_address();
-    uint32_t dest_addr = get_destination_address();
+    uint32_t source_address = get_source_address();
+    uint32_t destination_address = get_destination_address();
 
-    uint16_t cksum = tcp_header.calculate_tcp_checksum( src_addr, dest_addr, NULL, 0 );
+    uint16_t cksum = tcp_header.calculate_tcp_checksum(
+            source_address, destination_address, NULL, 0
+    );
     tcp_header.checksum( cksum );
 
     // Encode the request packet.
@@ -195,14 +215,18 @@ TcpHeader TcpPinger::create_tcp_header(
         const uint16_t sequence_number
 ) const
 {
+    // (5 words of 32 bits = 5 * 4 bytes = 20 bytes)
+    const uint8_t header_size_in_words = 5; // size in units of 32 bits
+    const uint16_t window_size_in_octets = 32768;
+
     // Create an TCP header for an ACK request.
     TcpHeader tcp_header;
     tcp_header.source_port( source_port ); // assign an random ephemeral port number
     tcp_header.destination_port( destination_port );
     tcp_header.sequence_number( sequence_number );
-    tcp_header.header_length( 5 ); // header size in units of 32 bits (5 words of 32 bits = 5 * 4 bytes = 20 bytes)
+    tcp_header.header_length( header_size_in_words );
     tcp_header.acknowledgment( true );
-    tcp_header.window_size( 32768 ); // window size
+    tcp_header.window_size( window_size_in_octets ); // window size
 
     return tcp_header;
 }
@@ -294,25 +318,27 @@ void TcpPinger::set_ping_status( TcpPinger::PingStatus ping_status )
  * is unreachable through the binded interface. Packets are sent only from
  * this interface.
  *
- * @param source_network_interface The network interface to bind the pinger.
+ * @param source_network_interface_name The network interface to bind the pinger.
  *
  * @return false if the bind failed.
  */
 bool TcpPinger::select_source_network_interface(
-        const string &source_network_interface
+        const string &source_network_interface_name
 )
 {
-    BOOST_ASSERT( !source_network_interface.empty() );
+    BOOST_ASSERT( !source_network_interface_name.empty() );
 
     int ret = ::setsockopt(
             Socket.native(),
             SOL_SOCKET,
             SO_BINDTODEVICE,
-            source_network_interface.c_str(),
-            source_network_interface.size()
+            source_network_interface_name.c_str(),
+            source_network_interface_name.size()
     );
     if ( ret == -1 )
     {
+        GlobalLogger.error() << "Error: could not bind pinger to interface "
+            << source_network_interface_name << endl;
         return false;
     }
 
index 87c3ca3..3826681 100644 (file)
@@ -43,7 +43,7 @@ class TcpPinger : public Pinger
 public:
     TcpPinger(
             boost::asio::io_service &io_service,
-            const std::string &source_network_interface,
+            const std::string &source_network_interface_name,
             const int echo_reply_timeout_in_sec
     );
     virtual ~TcpPinger();
@@ -63,7 +63,7 @@ private:
     };
 
 private:
-    uint32_t get_source_address() const;
+    uint32_t get_source_address();
     uint32_t get_destination_address() const;
     void set_destination_endpoint( const std::string &destination_ip );
 
@@ -86,7 +86,7 @@ private:
     void set_ping_status( TcpPinger::PingStatus ping_status );
 
     bool select_source_network_interface(
-            const std::string &source_network_interface
+            const std::string &source_network_interface_name
     );
 
 private:
@@ -96,6 +96,8 @@ private:
     boost::asio::ip::tcp_raw_protocol::endpoint DestinationEndpoint;
     /// the socket object
     boost::asio::ip::tcp_raw_protocol::socket Socket;
+    /// the network interface name which the socket is attached
+    std::string SourceNetworkInterfaceName;
     /// the timer of TCP segment receive, triggers the timeout to avoid infinite
     /// wait
     boost::asio::deadline_timer TcpSegmentReceiveTimer;
@@ -107,7 +109,7 @@ private:
     boost::posix_time::ptime TimeSent;
     /// the buffer where the data received will be placed
     boost::asio::streambuf ReplyBuffer;
-    /// Flag to indicate if we got a reply or not
+    /// flag to indicate if we got a reply or not
     /// number of replies to the ICMP echo request
     bool ReceivedReply;
     /// the amount of time to wait for the reply