Changed TCP ping method.
authorGuilherme Maciel Ferreira <guilherme.maciel.ferreira@gmail.com>
Sat, 14 Apr 2012 01:29:21 +0000 (22:29 -0300)
committerGuilherme Maciel Ferreira <guilherme.maciel.ferreira@gmail.com>
Sat, 14 Apr 2012 01:29:21 +0000 (22:29 -0300)
- SYN segments pass easily through firewalls and it is replied both when the
  port is open as well as when it is closed, serving better to the purpose of
  checking if the link reaches a given host. Whereas ACK segments do not get
  replied if the other end is closed or has a firewall in the middle.
- A thoroughly discussion regarding SYN x ACK is available at
  http://www.networkuptime.com/nmap/page4-4.shtml
  http://www.networkuptime.com/nmap/page4-5.shtml

Readme
src/tcp/tcpipv4segment.cpp
src/tcp/tcpipv4segment.h
src/tcp/tcpipv6segment.cpp
src/tcp/tcpipv6segment.h
src/tcp/tcppinger.cpp
src/tcp/tcpsegment.h
src/tcp/tcpsegmentfactory.cpp
src/tcp/tcpsegmentfactory.h

diff --git a/Readme b/Readme
index d0a6b63..374c87b 100644 (file)
--- a/Readme
+++ b/Readme
@@ -15,8 +15,8 @@ through pings to them.
 
 1.1. Rationale
 ---------------------------------------
-The application uses ICMP echo requests messages, or TCP segments to verify
-whether a given host is available (up) or not (down).
+The application uses ICMP echo requests messages, or TCP SYN segments, to
+verify whether a given host is available (up) or not (down).
 
 The host's address can be an IP or a DNS.
 
@@ -111,4 +111,4 @@ following:
 [3]  http://en.wikipedia.org/wiki/Internet_Control_Message_Protocol
 [4]  http://www.boost.org/doc/libs/1_45_0/doc/html/boost_asio.html
 [5]  http://www.boost.org/doc/libs/1_45_0/doc/html/program_options.html
-[6]  http://www.networkuptime.com/nmap/page4-4.shtml
+[6]  http://www.networkuptime.com/nmap/page4-5.shtml
index f9071c1..a05d5fe 100644 (file)
@@ -133,6 +133,57 @@ void TcpIpv4Segment::print_rst_reply(
 }
 
 /**
+ * @brief Convenience method to check if this segment, matching the arguments,
+ * is a SYN/ACK reply.
+ *
+ * @param source_address The source address.
+ *
+ * @return @c true if this packet is a SYN/ACK reply, or @c false otherwise.
+ */
+bool TcpIpv4Segment::match_syn_ack_reply(
+        const address &source_address
+) const
+{
+    BOOST_ASSERT( source_address.is_v4() );
+
+    Ipv4Header ipv4_header = get_ip_header();
+    TcpHeader tcp_header = get_tcp_header();
+
+    bool match_synchronize = tcp_header.get_synchronize();
+    bool match_acknowledgment = tcp_header.get_acknowledgment();
+    bool match_address = ipv4_header.get_source_address() == source_address;
+
+    return ( match_synchronize && match_acknowledgment && match_address );
+}
+
+/**
+ * @brief Prints the SYN/ACK reply.
+ *
+ * @param time_segment_sent The time when this segment was sent.
+ *
+ * @return void
+ */
+void TcpIpv4Segment::print_syn_ack_reply(
+        const ptime &time_segment_sent
+) const
+{
+    Ipv4Header ipv4_header = get_ip_header();
+    TcpHeader tcp_header = get_tcp_header();
+
+    uint32_t sequence_number = tcp_header.get_sequence_number();
+    int ttl = ipv4_header.get_time_to_live();
+    ptime now = microsec_clock::universal_time();
+    time_resolution_traits_adapted64_impl::int_type elapsed_time =
+            (now - time_segment_sent).total_milliseconds();
+
+    GlobalLogger.info() << "SYN/ACK from " << ipv4_header.get_source_address()
+        << ": tcp_seq=" << sequence_number
+        << ", ttl=" << ttl
+        << " time=" << elapsed_time << " ms"
+        << endl;
+}
+
+/**
  * @brief Read the TCP Segment from the @c istream.
  *
  * @param is The input stream.
index 09600a7..5393348 100644 (file)
@@ -97,6 +97,14 @@ public:
             const boost::posix_time::ptime &time_segment_sent
     ) const;
 
+    virtual bool match_syn_ack_reply(
+            const boost::asio::ip::address &source_address
+    ) const;
+
+    virtual void print_syn_ack_reply(
+            const boost::posix_time::ptime &time_packet_sent
+    ) const;
+
     virtual bool read( std::istream &is );
     virtual bool write( std::ostream &os ) const;
 
index 499f896..ac706b4 100644 (file)
@@ -133,6 +133,57 @@ void TcpIpv6Segment::print_rst_reply(
 }
 
 /**
+ * @brief Convenience method to check if this segment, matching the arguments,
+ * is a SYN/ACK reply.
+ *
+ * @param source_address The source address.
+ *
+ * @return @c true if this packet is a SYN/ACK reply, or @c false otherwise.
+ */
+bool TcpIpv6Segment::match_syn_ack_reply(
+        const address &source_address
+) const
+{
+    BOOST_ASSERT( source_address.is_v6() );
+
+    Ipv6Header ipv6_header = get_ip_header();
+    TcpHeader tcp_header = get_tcp_header();
+
+    bool match_synchronize = tcp_header.get_synchronize();
+    bool match_acknowledgment = tcp_header.get_acknowledgment();
+    bool match_address = ipv6_header.get_source_address() == source_address;
+
+    return ( match_synchronize && match_acknowledgment && match_address );
+}
+
+/**
+ * @brief Prints the SYN/ACK reply.
+ *
+ * @param time_segment_sent The time when this segment was sent.
+ *
+ * @return void
+ */
+void TcpIpv6Segment::print_syn_ack_reply(
+        const ptime &time_segment_sent
+) const
+{
+    Ipv6Header ipv6_header = get_ip_header();
+    TcpHeader tcp_header = get_tcp_header();
+
+    uint32_t sequence_number = tcp_header.get_sequence_number();
+    int hops = ipv6_header.get_hop_limit();
+    ptime now = microsec_clock::universal_time();
+    time_resolution_traits_adapted64_impl::int_type elapsed_time =
+            (now - time_segment_sent).total_milliseconds();
+
+    GlobalLogger.info() << "SYN/ACK from " << ipv6_header.get_source_address()
+        << ": tcp_seq=" << sequence_number
+        << ", hops=" << hops
+        << " time=" << elapsed_time << " ms"
+        << endl;
+}
+
+/**
  * @brief Read the TCP Segment from the @c istream.
  *
  * @param is The input stream.
index e6030a8..54940df 100644 (file)
@@ -111,6 +111,14 @@ public:
             const boost::posix_time::ptime &time_segment_sent
     ) const;
 
+    virtual bool match_syn_ack_reply(
+            const boost::asio::ip::address &source_address
+    ) const;
+
+    virtual void print_syn_ack_reply(
+            const boost::posix_time::ptime &time_packet_sent
+    ) const;
+
     virtual bool read( std::istream &is );
     virtual bool write( std::ostream &os ) const;
 
index 5ed53d6..9a1b466 100644 (file)
@@ -173,12 +173,12 @@ void TcpPinger::start_send()
 {
     ++SequenceNumber;
 
-    // Create a TCP header for an ACK request.
+    // Create a TCP header for a SYN request.
     address source_address = get_source_address();
     address destination_address = get_destination_address();
     uint16_t source_port = get_source_port();
     uint16_t destination_port = get_destination_port();
-    TcpSegmentItem tcp_segment = TcpSegmentFactory::create_tcp_segment_ack_request(
+    TcpSegmentItem tcp_segment = TcpSegmentFactory::create_tcp_segment_syn_request(
             Protocol,
             source_address, destination_address,
             source_port, destination_port, SequenceNumber );
@@ -286,8 +286,9 @@ void TcpPinger::handle_receive_tcp_segment( const size_t &bytes_transferred )
         // Decode the reply segment.
         TcpSegmentItem tcp_segment = TcpSegmentFactory::create_tcp_segment( Protocol, is );
 
-        // Filter out only the TCP reset (RST) replies. Note that the sequence
-        // number from RST does not match the sent ACK's sequence number.
+        // The TCP SYN ping will receive a RST if the port is closed.
+        // Filter out the TCP reset (RST) replies. Note that the sequence
+        // number from RST does not match the sent SYN's sequence number.
         if ( tcp_segment->match_rst_reply( DestinationEndpoint.address() ) )
         {
             ReceivedReply = true;
@@ -298,6 +299,19 @@ void TcpPinger::handle_receive_tcp_segment( const size_t &bytes_transferred )
 
             TcpSegmentReceiveTimer.cancel();                                                                                                                //lint !e534
         }
+        // The TCP SYN ping will receive a SYN/ACK if the port is open.
+        // Filter out the TCP synchronize (SYN/ACK) replies. Note that the sequence
+        // number from SYN/ACK does not match the sent SYN's sequence number.
+        else if ( tcp_segment->match_syn_ack_reply( DestinationEndpoint.address() ) )
+        {
+            ReceivedReply = true;
+
+            tcp_segment->print_syn_ack_reply( TimeSent );
+
+            set_ping_status( PingStatus_SuccessReply );
+
+            TcpSegmentReceiveTimer.cancel();                                                                                                                //lint !e534
+        }
         // Unknown TCP reply, start another receive till timeout
         else
         {
index 4788d10..7df5b6c 100644 (file)
@@ -44,6 +44,14 @@ public:
             const boost::posix_time::ptime &time_packet_sent
     ) const = 0;
 
+    virtual bool match_syn_ack_reply(
+            const boost::asio::ip::address &source_address
+    ) const = 0;
+
+    virtual void print_syn_ack_reply(
+            const boost::posix_time::ptime &time_packet_sent
+    ) const = 0;
+
     virtual bool read( std::istream &is ) = 0;
     virtual bool write( std::ostream &os ) const = 0;
 
index 69f6345..da90b30 100644 (file)
@@ -78,7 +78,7 @@ TcpSegmentItem TcpSegmentFactory::create_tcp_segment(
 }
 
 /**
- * @brief Creates a TCP ACK request segment.
+ * @brief Creates a TCP SYN request segment.
  *
  * @param protocol The segment's network layer protocol, IPv4 or IPv6.
  * @param source_address The local address originating the ping.
@@ -86,11 +86,11 @@ TcpSegmentItem TcpSegmentFactory::create_tcp_segment(
  * @param source_port The local TCP port.
  * @param destination_port The remote TCP port.
  * @param sequence_number The segment's sequence number to aid in matching RST
- * replies to this ACK request. May be zero.
+ * or SYN/ACK replies to this SYN request. May be zero.
  *
- * @return A TCP ACK request segment object.
+ * @return A TCP SYN request segment object.
  */
-TcpSegmentItem TcpSegmentFactory::create_tcp_segment_ack_request(
+TcpSegmentItem TcpSegmentFactory::create_tcp_segment_syn_request(
         const tcp_raw_protocol::socket::protocol_type &protocol,
         const address &source_address,
         const address &destination_address,
@@ -105,13 +105,13 @@ TcpSegmentItem TcpSegmentFactory::create_tcp_segment_ack_request(
 
     if ( tcp_raw_protocol::v4() == protocol )
     {
-        tcp_segment = create_tcp_ipv4_segment_ack_request(
+        tcp_segment = create_tcp_ipv4_segment_syn_request(
                 source_address, destination_address,
                 source_port, destination_port, sequence_number );
     }
     else if ( tcp_raw_protocol::v6() == protocol )
     {
-        tcp_segment = create_tcp_ipv6_segment_ack_request(
+        tcp_segment = create_tcp_ipv6_segment_syn_request(
                 source_address, destination_address,
                 source_port, destination_port, sequence_number );
     }
@@ -124,18 +124,18 @@ TcpSegmentItem TcpSegmentFactory::create_tcp_segment_ack_request(
 }
 
 /**
- * @brief Creates a TCP ACK request segment over IPv4.
+ * @brief Creates a TCP SYN request segment over IPv4.
  *
  * @param source_address The local address originating the ping.
  * @param destination_address The remote address where to ping.
  * @param source_port The local TCP port.
  * @param destination_port The remote TCP port.
  * @param sequence_number The segment's sequence number to aid in matching RST
- * replies to this ACK request. May be zero.
+ * or SYN/ACK replies to this SYN request. May be zero.
  *
- * @return A TCP ACK request segment object.
+ * @return A TCP SYN request segment object.
  */
-TcpSegmentItem TcpSegmentFactory::create_tcp_ipv4_segment_ack_request(
+TcpSegmentItem TcpSegmentFactory::create_tcp_ipv4_segment_syn_request(
         const address &source_address,
         const address &destination_address,
         const uint16_t source_port,
@@ -150,13 +150,13 @@ TcpSegmentItem TcpSegmentFactory::create_tcp_ipv4_segment_ack_request(
     const uint8_t header_size_in_words = 5; // size in units of 32 bits
     const uint16_t window_size_in_octets = 32768;
 
-    // Create a TCP header for an ACK request.
+    // Create a TCP header for a SYN request.
     TcpHeader tcp_header;
     tcp_header.set_source_port( source_port ); // assign a random ephemeral port number
     tcp_header.set_destination_port( destination_port );
     tcp_header.set_sequence_number( sequence_number );
     tcp_header.set_header_length( header_size_in_words );
-    tcp_header.set_acknowledgment( true );
+    tcp_header.set_synchronize( true );
     tcp_header.set_window_size( window_size_in_octets ); // window size
 
     // Calculate the checksum of the TCP header
@@ -173,18 +173,18 @@ TcpSegmentItem TcpSegmentFactory::create_tcp_ipv4_segment_ack_request(
 }
 
 /**
- * @brief Creates a TCP ACK request segment over IPv6.
+ * @brief Creates a TCP SYN request segment over IPv6.
  *
  * @param source_address The local address originating the ping.
  * @param destination_address The remote address where to ping.
  * @param source_port The local TCP port.
  * @param destination_port The remote TCP port.
  * @param sequence_number The segment's sequence number to aid in matching RST
- * replies to this ACK request. May be zero.
+ * or SYN/ACK replies to this SYN request. May be zero.
  *
- * @return A TCP ACK request segment object.
+ * @return A TCP SYN request segment object.
  */
-TcpSegmentItem TcpSegmentFactory::create_tcp_ipv6_segment_ack_request(
+TcpSegmentItem TcpSegmentFactory::create_tcp_ipv6_segment_syn_request(
         const address &source_address,
         const address &destination_address,
         const uint16_t source_port,
@@ -199,13 +199,13 @@ TcpSegmentItem TcpSegmentFactory::create_tcp_ipv6_segment_ack_request(
     const uint8_t header_size_in_words = 5; // size in units of 32 bits
     const uint16_t window_size_in_octets = 32768;
 
-    // Create a TCP header for an ACK request.
+    // Create a TCP header for a SYN request.
     TcpHeader tcp_header;
     tcp_header.set_source_port( source_port ); // assign a random ephemeral port number
     tcp_header.set_destination_port( destination_port );
     tcp_header.set_sequence_number( sequence_number );
     tcp_header.set_header_length( header_size_in_words );
-    tcp_header.set_acknowledgment( true );
+    tcp_header.set_synchronize( true );
     tcp_header.set_window_size( window_size_in_octets ); // window size
 
     // Calculate the checksum
index e81677a..a5b18a1 100644 (file)
@@ -45,7 +45,7 @@ public:
             const boost::asio::ip::tcp_raw_protocol::socket::protocol_type &protocol,
             std::istream &is
     );
-    static TcpSegmentItem create_tcp_segment_ack_request(
+    static TcpSegmentItem create_tcp_segment_syn_request(
             const boost::asio::ip::tcp_raw_protocol::socket::protocol_type &protocol,
             const boost::asio::ip::address &source_address,
             const boost::asio::ip::address &destination_address,
@@ -55,14 +55,14 @@ public:
     );
 
 private:
-    static TcpSegmentItem create_tcp_ipv4_segment_ack_request(
+    static TcpSegmentItem create_tcp_ipv4_segment_syn_request(
             const boost::asio::ip::address &source_address,
             const boost::asio::ip::address &destination_address,
             const uint16_t source_port,
             const uint16_t destination_port,
             const uint16_t sequence_number
     );
-    static TcpSegmentItem create_tcp_ipv6_segment_ack_request(
+    static TcpSegmentItem create_tcp_ipv6_segment_syn_request(
             const boost::asio::ip::address &source_address,
             const boost::asio::ip::address &destination_address,
             const uint16_t source_port,