From 2cdf0f6b293963143352719a74a827b9cb8db5a0 Mon Sep 17 00:00:00 2001 From: Matias Date: Fri, 12 Aug 2011 20:17:21 -0300 Subject: [PATCH] Fixed TCP checksum. Documented pdu.h. --- include/ip.h | 5 ++- include/pdu.h | 84 +++++++++++++++++++++++++++++++++++++------- include/tcp.h | 16 +++++---- src/ip.cpp | 3 +- src/packetsender.cpp | 1 + src/pdu.cpp | 8 ++--- src/tcp.cpp | 41 +++++++++++++++++---- 7 files changed, 124 insertions(+), 34 deletions(-) diff --git a/include/ip.h b/include/ip.h index 0cce164..3646c81 100644 --- a/include/ip.h +++ b/include/ip.h @@ -164,12 +164,11 @@ namespace Tins { static const uint8_t DEFAULT_TTL; void init_ip_fields(); - void write_serialization(uint8_t *buffer, uint32_t total_sz); + void write_serialization(uint8_t *buffer, uint32_t total_sz, PDU *parent); iphdr _ip; std::vector _ip_options; - uint32_t _options_size; - uint32_t _padded_options_size; + uint32_t _options_size, _padded_options_size; }; }; diff --git a/include/pdu.h b/include/pdu.h index 9aad675..12fae71 100644 --- a/include/pdu.h +++ b/include/pdu.h @@ -30,37 +30,95 @@ namespace Tins { class PacketSender; + /** \brief PDU is the base class for protocol data units. + * + * Every PDU implementation must inherit this one. PDUs can be serialized, + * therefore allowing a PacketSender to send them through sockets. PDUs + * are created upwards: upper layers will be children of the lower ones. + * Each PDU must provide its flag identifier. This will be most likely added + * to its parent's data, hence it should be a valid identifier. For example, + * IP should provide IPPROTO_IP. + */ class PDU { public: + /** \brief PDU constructor + * + * Must be called by subclasses in their constructors. + * \param flag The flag identifier for the subclass' PDU. + * \param next_pdu The child PDU. Can be obviated. + */ PDU(uint32_t flag, PDU *next_pdu = 0); virtual ~PDU(); - /* This PDU's header size only. */ + /** \brief The header's size + */ virtual uint32_t header_size() const = 0; - /* This PDU's trailer size only. Defaults to 0. */ + + /** \brief Trailer's size. + * + * Some protocols require a trailer(like Ethernet). This defaults to 0. + */ virtual uint32_t trailer_size() const { return 0; } - /* The size of the whole chain of PDUs, including this one. */ - inline uint32_t size() const; + + /** \brief The whole chain of PDU's size, including this one. + * + * Returns the sum of this and all children PDUs' size. + */ + uint32_t size() const; + + /** \brief This PDU's type flag identifier. + * + */ inline uint32_t flag() const { return _flag; } + + /** \brief The child PDU. + */ inline const PDU *inner_pdu() const { return _inner_pdu; } + /** \brief Sets the flag identifier. + */ void flag(uint32_t new_flag); - /* When setting a new inner_pdu, the instance takes - * ownership of the object, therefore deleting it when - * it's no longer required. */ + + /** \brief Sets the child PDU. + * + * \param next_pdu The new child PDU. + * When setting a new inner_pdu, the instance takesownership of + * the object, therefore deleting it when it's no longer required. + */ void inner_pdu(PDU *next_pdu); - /* Serializes the whole chain of PDU's, including this one. */ + + /** \brief Serializes the whole chain of PDU's, including this one. + * + * \param sz The size of the buffer must be returned through this parameter. + * The buffer returned must be deleted by the user using + * operator delete[]. + */ uint8_t *serialize(uint32_t &sz); - /* */ - virtual bool send(PacketSender* sender) {return false;} + /** \brief Send the stack of PDUs through a PacketSender. + * + * This method will be called only for the PDU on the bottom of the stack, + * therefore it should only implement this method if it can be sent. + * PacketSender implements specific methods to send packets which start + * on every valid TCP/IP stack layer; this should only be a proxy for + * those methods. + * \param sender The PacketSender which will send the packet. + */ + virtual bool send(PacketSender* sender) { return false; } protected: /* Serialize this PDU storing the result in buffer. */ - void serialize(uint8_t *buffer, uint32_t total_sz); + void serialize(uint8_t *buffer, uint32_t total_sz, PDU *parent); - /* Each PDU's own implementation of serialization. */ - virtual void write_serialization(uint8_t *buffer, uint32_t total_sz) = 0; + /** \brief Serialices this TCP PDU. + * + * Each PDU must override this method and implement it's own + * serialization. + * \param buffer The buffer in which the PDU will be serialized. + * \param total_sz The size available in the buffer. + * \param parent The PDU that's one level below this one on the stack. Might be 0. + */ + virtual void write_serialization(uint8_t *buffer, uint32_t total_sz, PDU *parent) = 0; private: uint32_t _flag; PDU *_inner_pdu; diff --git a/include/tcp.h b/include/tcp.h index aad83b0..517dc72 100644 --- a/include/tcp.h +++ b/include/tcp.h @@ -185,12 +185,6 @@ namespace Tins { * payload and options size. */ uint32_t header_size() const; - - /** \brief Serialices this TCP PDU. - * \param buffer The buffer in which the PDU will be serialized. - * \param total_sz The size available in the buffer. - */ - void write_serialization(uint8_t *buffer, uint32_t total_sz); private: struct tcphdr { uint16_t sport; @@ -239,7 +233,15 @@ namespace Tins { static const uint16_t DEFAULT_WINDOW; - uint16_t do_checksum(uint8_t *start, uint8_t *end) const; + /** \brief Serialices this TCP PDU. + * \param buffer The buffer in which the PDU will be serialized. + * \param total_sz The size available in the buffer. + * \param parent The PDU that's one level below this one on the stack. + */ + void write_serialization(uint8_t *buffer, uint32_t total_sz, PDU *parent); + + uint32_t do_checksum(uint8_t *start, uint8_t *end) const; + uint32_t pseudoheader_checksum(uint32_t source_ip, uint32_t dest_ip) const; tcphdr _tcp; std::vector _options; diff --git a/src/ip.cpp b/src/ip.cpp index 9aecd3b..e10cd59 100644 --- a/src/ip.cpp +++ b/src/ip.cpp @@ -54,6 +54,7 @@ void Tins::IP::init_ip_fields() { _ip.ihl = sizeof(iphdr) / sizeof(uint32_t); _ip.ttl = DEFAULT_TTL; _options_size = 0; + _padded_options_size = 0; } /* Setters */ @@ -166,7 +167,7 @@ bool Tins::IP::send(PacketSender* sender) { return sender->send_l3(this, (const struct sockaddr*)&link_addr, sizeof(link_addr)); } -void Tins::IP::write_serialization(uint8_t *buffer, uint32_t total_sz) { +void Tins::IP::write_serialization(uint8_t *buffer, uint32_t total_sz, PDU *) { uint32_t my_sz = header_size(); uint32_t new_flag; assert(total_sz >= my_sz); diff --git a/src/packetsender.cpp b/src/packetsender.cpp index 23829f2..10f40a2 100644 --- a/src/packetsender.cpp +++ b/src/packetsender.cpp @@ -77,6 +77,7 @@ bool Tins::PacketSender::send_l3(PDU *pdu, const struct sockaddr* link_addr, uin int sock = _sockets[IP_SOCKET]; uint8_t *buffer = pdu->serialize(sz); ret_val = (sendto(sock, buffer, sz, 0, link_addr, len_link_addr) != -1); + std::cout << "Ret_val: " << ret_val << "\n"; delete[] buffer; } diff --git a/src/pdu.cpp b/src/pdu.cpp index 9b8e2fd..e770fc6 100644 --- a/src/pdu.cpp +++ b/src/pdu.cpp @@ -52,16 +52,16 @@ void Tins::PDU::inner_pdu(PDU *next_pdu) { uint8_t *Tins::PDU::serialize(uint32_t &sz) { sz = size(); uint8_t *buffer = new uint8_t[sz]; - serialize(buffer, sz); + serialize(buffer, sz, 0); return buffer; } -void Tins::PDU::serialize(uint8_t *buffer, uint32_t total_sz) { +void Tins::PDU::serialize(uint8_t *buffer, uint32_t total_sz, PDU *parent) { uint32_t sz = header_size() + trailer_size(); /* Must not happen... */ assert(total_sz >= sz); if(_inner_pdu) - _inner_pdu->serialize(buffer + header_size(), total_sz - sz); - write_serialization(buffer, total_sz); + _inner_pdu->serialize(buffer + header_size(), total_sz - sz, this); + write_serialization(buffer, total_sz, parent); } diff --git a/src/tcp.cpp b/src/tcp.cpp index a50713d..a0e9e2a 100644 --- a/src/tcp.cpp +++ b/src/tcp.cpp @@ -21,10 +21,12 @@ #include #include +#include //borrame #ifndef WIN32 #include #endif #include "tcp.h" +#include "ip.h" #include "utils.h" @@ -125,10 +127,28 @@ void Tins::TCP::add_option(Options tcp_option, uint8_t length, uint8_t *data) { _total_options_size = (padding) ? _options_size - padding + 4 : _options_size; } -uint16_t Tins::TCP::do_checksum(uint8_t *start, uint8_t *end) const { - unsigned checksum(0); - while(start < end) - checksum += *(start++); +uint32_t Tins::TCP::do_checksum(uint8_t *start, uint8_t *end) const { + uint32_t checksum(0); + uint16_t *ptr = (uint16_t*)start, *last = (uint16_t*)end, padding(0); + if(((end - start) & 1) == 1) { + last = (uint16_t*)end - 1; + padding = *(end - 1) << 8; + } + while(ptr < last) + checksum += Utils::net_to_host_s(*(ptr++)); + return checksum + padding; +} + +uint32_t Tins::TCP::pseudoheader_checksum(uint32_t source_ip, uint32_t dest_ip) const { + uint32_t checksum(0), len(header_size()); + source_ip = Utils::net_to_host_l(source_ip); + dest_ip = Utils::net_to_host_l(dest_ip); + uint16_t *ptr = (uint16_t*)&source_ip; + + checksum += *ptr + ptr[1]; + ptr = (uint16_t*)&dest_ip; + checksum += *ptr + ptr[1]; + checksum += IPPROTO_TCP + len; return checksum; } @@ -136,7 +156,7 @@ uint32_t Tins::TCP::header_size() const { return sizeof(tcphdr) + _payload_size + _total_options_size; } -void Tins::TCP::write_serialization(uint8_t *buffer, uint32_t total_sz) { +void Tins::TCP::write_serialization(uint8_t *buffer, uint32_t total_sz, PDU *parent) { assert(total_sz >= header_size()); uint8_t *tcp_start = buffer; buffer += sizeof(tcphdr); @@ -153,7 +173,16 @@ void Tins::TCP::write_serialization(uint8_t *buffer, uint32_t total_sz) { } memcpy(buffer, _payload, _payload_size); - _tcp.check = Utils::net_to_host_s(do_checksum(tcp_start + sizeof(tcphdr), buffer)); + buffer += _payload_size; + IP *ip_packet = dynamic_cast(parent); + if(ip_packet) { + _tcp.check = 0; + uint32_t checksum = pseudoheader_checksum(ip_packet->source_address(), ip_packet->dest_address()) + + do_checksum(tcp_start + sizeof(tcphdr), buffer) + do_checksum((uint8_t*)&_tcp, ((uint8_t*)&_tcp) + sizeof(tcphdr)); + while (checksum >> 16) + checksum = (checksum & 0xffff)+(checksum >> 16); + _tcp.check = Utils::net_to_host_s(~checksum); + } memcpy(tcp_start, &_tcp, sizeof(tcphdr)); }