Bring aboard the TCP header and support classes
authorGuilherme Maciel Ferreira <guilherme.maciel.ferreira@gmail.com>
Sun, 10 Jul 2011 03:20:30 +0000 (00:20 -0300)
committerGuilherme Maciel Ferreira <guilherme.maciel.ferreira@gmail.com>
Sun, 10 Jul 2011 03:20:30 +0000 (00:20 -0300)
- Tested with a TCP ping prototype, solved the checksum issue
- Needs to be polished

src/CMakeLists.txt
src/ip/pseudoipv4header.h [new file with mode: 0644]
src/tcp/tcpheader.h [new file with mode: 0644]
src/tcp/tcppinger.cpp

index 6442b0f..9418434 100644 (file)
@@ -9,6 +9,9 @@ find_package(Boost 1.44 COMPONENTS filesystem program_options system thread REQU
 include_directories(${Boost_INCLUDE_DIRS})
 link_directories(${Boost_LIBRARY_DIRS})
 
+# package: boost-custom
+include_directories(${CMAKE_SOURCE_DIR}/lib/boost-custom)
+
 # package: boost-net-dns
 include_directories(${CMAKE_SOURCE_DIR}/lib/boost-net-dns)
 
diff --git a/src/ip/pseudoipv4header.h b/src/ip/pseudoipv4header.h
new file mode 100644 (file)
index 0000000..52745b1
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+The software in this package is distributed under the GNU General
+Public License version 2 (with a special exception described below).
+
+A copy of GNU General Public License (GPL) is included in this distribution,
+in the file COPYING.GPL.
+
+As a special exception, if other files instantiate templates or use macros
+or inline functions from this file, or you compile this file and link it
+with other works to produce a work based on this file, this file
+does not by itself cause the resulting work to be covered
+by the GNU General Public License.
+
+However the source code for this file must still be made available
+in accordance with section (3) of the GNU General Public License.
+
+This exception does not invalidate any other reasons why a work based
+on this file might be covered by the GNU General Public License.
+*/
+
+#ifndef PSEUDO_IPV4_HEADER_H
+#define PSEUDO_IPV4_HEADER_H
+
+//-----------------------------------------------------------------------------
+// PseudoIpv4Header
+//-----------------------------------------------------------------------------
+// Pseudo IPv4 header format used by TCP and UDP checksums:
+//
+// 0               8               16                             31
+// +--------------------------------------------------------------+      ---
+// |                                                              |       ^
+// |                      source IPv4 address                     |       |
+// |                                                              |       |
+// +--------------------------------------------------------------+       |
+// |                                                              |       |
+// |                   destination IPv4 address                   |   12 bytes
+// |                                                              |       |
+// +---------------+---------------+------------------------------+       |
+// |               |               |                              |       |
+// |      zero     |   protocol    |        header length         |       |
+// |               |               |                              |       v
+// +---------------+---------------+------------------------------+      ---
+//
+//-----------------------------------------------------------------------------
+
+class PseudoIpv4Header
+{
+public:
+    uint32_t source_address;
+    uint32_t destination_address;
+    uint8_t zero_byte;
+    uint8_t protocol; // protocol
+    uint16_t header_length; // TCP or UDP header length
+};
+
+#endif /* PSEUDO_IPV4_HEADER_H */
diff --git a/src/tcp/tcpheader.h b/src/tcp/tcpheader.h
new file mode 100644 (file)
index 0000000..adf07a0
--- /dev/null
@@ -0,0 +1,323 @@
+/*
+The software in this package is distributed under the GNU General
+Public License version 2 (with a special exception described below).
+
+A copy of GNU General Public License (GPL) is included in this distribution,
+in the file COPYING.GPL.
+
+As a special exception, if other files instantiate templates or use macros
+or inline functions from this file, or you compile this file and link it
+with other works to produce a work based on this file, this file
+does not by itself cause the resulting work to be covered
+by the GNU General Public License.
+
+However the source code for this file must still be made available
+in accordance with section (3) of the GNU General Public License.
+
+This exception does not invalidate any other reasons why a work based
+on this file might be covered by the GNU General Public License.
+*/
+#ifndef TCP_HEADER_H
+#define TCP_HEADER_H
+
+#include <algorithm>
+
+#include <boost/asio/ip/tcp_raw_protocol.hpp>
+
+#include "ip/pseudoipv4header.h"
+
+// TCP Segment header.
+//
+// The wire format of a TCP header is:
+//
+// 0        4       8              16               24           31
+// +-------------------------------+------------------------------+      ---
+// |                               |                              |       ^
+// |         source port           |     destination port         |       |
+// |                               |                              |       |
+// +--------------------------------------------------------------+       |
+// |                                                              |       |
+// |                        sequence number                       |       |
+// |                                                              |       |
+// +--------------------------------------------------------------+       |
+// |                                                              |       |
+// |              acknowledgment number (if ACK set)              |   20 bytes
+// |                                                              |       |
+// +-------+-------+-+-+-+-+-+-+-+-+------------------------------+       |
+// | data  | reser-|C|E|U|A|P|R|S|F|                              |       |
+// |offset | ved   |W|C|R|C|S|S|Y|I|        window size           |       |
+// |       |       |R|E|G|K|H|T|N|N|                              |       |
+// +---------------+-+-+-+-+-+-+-+-+------------------------------+       |
+// |                               |                              |       |
+// |          checksum             | urgent pointer (if URG set)  |       |
+// |                               |                              |       v
+// +-----------------------------------------------+--------------+      ---
+// |                                               |              |       ^
+// |                                               |              |       |
+// |          options (if Data Offset > 5)         |   padding    /    0 - 40
+// |                                               |              /     bytes
+// |                                               |              |       |
+// |                                               |              |       v
+// +-----------------------------------------------+--------------+      ---
+//
+//-----------------------------------------------------------------------------
+
+class TcpHeader
+{
+public:
+    TcpHeader()
+    {
+        std::fill( rep_, rep_ + sizeof(rep_), 0 );
+    }
+
+    uint16_t source_port() const
+    {
+        return decode( 0, 1 );
+    }
+
+    uint16_t destination_port() const
+    {
+        return decode( 2, 3 );
+    }
+
+    uint32_t sequence_number() const
+    {
+        uint32_t seq_num = (uint32_t) (
+                rep_[4] << 24 |
+                rep_[5] << 16 |
+                rep_[6] << 8 |
+                rep_[7]
+        );
+
+        return seq_num;
+    }
+
+    uint32_t acknowledgment_number() const
+    {
+        uint32_t ack_num = (uint32_t) (
+                rep_[8] << 24 |
+                rep_[9] << 16 |
+                rep_[10] << 8 |
+                rep_[11]
+        );
+
+        return ack_num;
+    }
+
+    uint8_t header_length() const
+    {
+        return (rep_[12] & 0xF0) >> 4;
+    }
+
+    bool congestion_window_reduced() const
+    {
+        return (rep_[13] & 0x80);
+    }
+
+    bool ecn_echo() const
+    {
+        return (rep_[13] & 0x40);
+    }
+
+    bool urgent() const
+    {
+        return (rep_[13] & 0x20);
+    }
+
+    bool acknowledgment() const
+    {
+        return (rep_[13] & 0x10);
+    }
+
+    bool push() const
+    {
+        return (rep_[13] & 0x08);
+    }
+
+    bool reset() const
+    {
+        return (rep_[13] & 0x04);
+    }
+
+    bool synchronize() const
+    {
+        return (rep_[13] & 0x02);
+    }
+
+    bool finish() const
+    {
+        return (rep_[13] & 0x01);
+    }
+
+    uint16_t window_size() const
+    {
+        return decode( 14, 15 );
+    }
+
+    uint16_t checksum() const
+    {
+        return decode( 16, 17 );
+    }
+
+    uint16_t calculate_tcp_checksum(
+            uint32_t src_addr,
+            uint32_t dest_addr,
+            char* payload_data,
+            uint16_t payload_size_in_bytes )
+    {
+        checksum( 0 );
+        uint16_t tcp_header_size_in_bytes = 20;
+
+        PseudoIpv4Header pseudo_header;
+        pseudo_header.source_address = htonl(src_addr);
+        pseudo_header.destination_address = htonl(dest_addr);
+        pseudo_header.zero_byte = 0;
+        pseudo_header.protocol = IPPROTO_TCP;
+        pseudo_header.header_length = htons(
+                tcp_header_size_in_bytes + payload_size_in_bytes
+        );
+        int pseudo_header_size_in_bytes = sizeof(PseudoIpv4Header);
+
+        uint8_t tcp_buffer[65536];
+        memset( tcp_buffer, 0, sizeof(tcp_buffer) );
+
+        memcpy( tcp_buffer, &pseudo_header, pseudo_header_size_in_bytes );
+        memcpy( tcp_buffer + pseudo_header_size_in_bytes, rep_,
+                tcp_header_size_in_bytes );
+        memcpy( tcp_buffer + pseudo_header_size_in_bytes + tcp_header_size_in_bytes,
+                payload_data, payload_size_in_bytes );
+
+        uint16_t cksum = calculate_checksum( (uint16_t *)
+            tcp_buffer,
+            pseudo_header_size_in_bytes + tcp_header_size_in_bytes + payload_size_in_bytes
+        );
+
+        cksum = ntohs( cksum );
+
+        return cksum;
+    }
+
+    void source_port( uint16_t port )
+    {
+        encode( 0, 1, port );
+    }
+
+    void destination_port( uint16_t port )
+    {
+        encode( 2, 3, port );
+    }
+
+    void sequence_number( uint32_t seq_num )
+    {
+        rep_[4] = (uint8_t) (seq_num >> 24) & 0xFF;
+        rep_[5] = (uint8_t) (seq_num >> 16) & 0xFF;
+        rep_[6] = (uint8_t) (seq_num >> 8) & 0xFF;
+        rep_[7] = (uint8_t) seq_num & 0xFF;
+    }
+
+    void acknowledgment_number( uint32_t ack_num )
+    {
+        rep_[8] = (uint8_t) (ack_num >> 24) & 0xFF;
+        rep_[9] = (uint8_t) (ack_num >> 16) & 0xFF;
+        rep_[10] = (uint8_t) (ack_num >> 8) & 0xFF;
+        rep_[11] = (uint8_t) ack_num & 0xFF;
+    }
+
+    void header_length( uint8_t offset )
+    {
+        uint8_t high_nimble = ( offset << 4 ) & 0xF0;
+        uint8_t lower_nimble = ( rep_[12] & 0x0F );
+        rep_[12] = high_nimble | lower_nimble;
+    }
+
+    void congestion_window_reduced( bool bit )
+    {
+        rep_[13] |= bit ? 0x80 : 0x0;
+    }
+
+    void ecn_echo( bool bit )
+    {
+        rep_[13] |= bit ? 0x40 : 0x0;
+    }
+
+    void urgent( bool bit )
+    {
+        rep_[13] |= bit ? 0x20 : 0x0;
+    }
+
+    void acknowledgment( bool bit )
+    {
+        rep_[13] |= bit ? 0x10 : 0x0;
+    }
+
+    void push( bool bit )
+    {
+        rep_[13] |= bit ? 0x08 : 0x0;
+    }
+
+    void reset( bool bit )
+    {
+        rep_[13] |= bit ? 0x04 : 0x0;
+    }
+
+    void synchronize( bool bit )
+    {
+        rep_[13] |= bit ? 0x02 : 0x0;
+    }
+
+    void finish( bool bit )
+    {
+        rep_[13] |= bit ? 0x01 : 0x0;
+    }
+
+    void window_size( uint16_t wnd_size )
+    {
+        encode( 14, 15, wnd_size );
+    }
+
+    void checksum( uint16_t sum )
+    {
+        encode( 16, 17, sum );
+    }
+
+    friend std::istream& operator>>( std::istream& is, TcpHeader& header )
+    {
+        return is.read( reinterpret_cast< char* > ( header.rep_ ), 20 );
+    }
+
+    friend std::ostream& operator<<( std::ostream& os, const TcpHeader& header )
+    {
+        return os.write( reinterpret_cast< const char* > ( header.rep_ ), 20 );
+    }
+
+private:
+    uint16_t decode( int a, int b ) const
+    {
+        return (rep_[a] << 8) + rep_[b];
+    }
+
+    void encode( int a, int b, uint16_t n )
+    {
+        rep_[a] = static_cast< uint8_t > ( n >> 8 );
+        rep_[b] = static_cast< uint8_t > ( n & 0xFF );
+    }
+
+    uint16_t calculate_checksum( uint16_t *word_array, int size )
+    {
+        unsigned long cksum = 0;
+        while ( size > 1 ) {
+            cksum += *word_array++;
+            size -= sizeof(word_array[ 0 ]);
+        }
+        if ( size )
+            cksum += *(uint8_t*) word_array;
+
+        cksum = (cksum >> 16) + (cksum & 0xffff);
+        cksum += (cksum >> 16);
+        return (uint16_t) (~cksum);
+    }
+
+    uint8_t rep_[60];
+};
+
+#endif /* TCP_HEADER_H */
index ced9c99..c2d1566 100644 (file)
@@ -21,6 +21,8 @@ on this file might be covered by the GNU General Public License.
 
 #include <boost/assert.hpp>
 
+#include "tcp/tcpheader.h"
+
 using namespace std;
 using boost::function;