From 22c5aae46c9fe02da4f5e342c15383a31473ca87 Mon Sep 17 00:00:00 2001 From: Guilherme Maciel Ferreira Date: Sun, 10 Jul 2011 00:20:30 -0300 Subject: [PATCH] Bring aboard the TCP header and support classes - Tested with a TCP ping prototype, solved the checksum issue - Needs to be polished --- src/CMakeLists.txt | 3 + src/ip/pseudoipv4header.h | 56 ++++++++ src/tcp/tcpheader.h | 323 +++++++++++++++++++++++++++++++++++++++++++++ src/tcp/tcppinger.cpp | 2 + 4 files changed, 384 insertions(+), 0 deletions(-) create mode 100644 src/ip/pseudoipv4header.h create mode 100644 src/tcp/tcpheader.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6442b0f..9418434 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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 index 0000000..52745b1 --- /dev/null +++ b/src/ip/pseudoipv4header.h @@ -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 index 0000000..adf07a0 --- /dev/null +++ b/src/tcp/tcpheader.h @@ -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 + +#include + +#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 */ diff --git a/src/tcp/tcppinger.cpp b/src/tcp/tcppinger.cpp index ced9c99..c2d1566 100644 --- a/src/tcp/tcppinger.cpp +++ b/src/tcp/tcppinger.cpp @@ -21,6 +21,8 @@ on this file might be covered by the GNU General Public License. #include +#include "tcp/tcpheader.h" + using namespace std; using boost::function; -- 1.7.1