diff --git a/include/icmp.h b/include/icmp.h new file mode 100644 index 0000000..f0db12c --- /dev/null +++ b/include/icmp.h @@ -0,0 +1,169 @@ +#ifndef __ICMP_H +#define __ICMP_H + + +#include "pdu.h" + +namespace Tins { + + /** \brief ICMP represents the ICMP PDU. + * + * ICMP is the representation of the ICMP PDU. Instances of this class + * must be sent over a level 3 PDU, this will otherwise fail. + */ + class ICMP : public PDU { + public: + /** \brief ICMP flags + */ + enum Flags { + ECHO_REPLY = 0, + DEST_UNREACHABLE = 3, + SOURCE_QUENCH = 4, + REDIRECT = 5, + ECHO_REQUEST = 8, + TIME_EXCEEDED = 11, + PARAM_PROBLEM = 12, + INFO_REQUEST = 15, + INFO_REPLY = 16 + }; + + /** \brief Creates an instance of ICMP. + * + * If no flag is specified, then ECHO_REPLY will be used. + * \param flag The type flag which will be set. + */ + ICMP(Flags flag = ECHO_REPLY); + + /** \brief Sets the code field. + * + * \param new_code The code which will be stored in the ICMP struct. + */ + void code(uint8_t new_code); + + /** \brief Sets the type field. + * + * \param new_code The type which will be stored in the ICMP struct. + */ + void type(uint8_t type); + + /** \brief Sets echo request flag for this PDU. + * + * \param id The identifier for this request. + * \param seq The sequence number for this request. + */ + void set_echo_request(uint16_t id, uint16_t seq); + + /** \brief Sets echo request flag for this PDU. + * + * This uses a global id and sequence number to fill the request's + * fields. + */ + void set_echo_request(); + + /** \brief Sets echo reply flag for this PDU. + * + * \param id The identifier for this request. + * \param seq The sequence number for this request. + */ + void set_echo_reply(uint16_t id, uint16_t seq); + + /** \brief Sets echo reply flag for this PDU. + * + * This uses a global id and sequence number to fill the request's + * fields. + */ + void set_echo_reply(); + + /** \brief Sets information request flag for this PDU. + * + * \param id The identifier for this request. + * \param seq The sequence number for this request. + */ + void set_info_request(uint16_t id, uint16_t seq); + + /** \brief Sets information reply flag for this PDU. + * + * \param id The identifier for this request. + * \param seq The sequence number for this request. + */ + void set_info_reply(uint16_t id, uint16_t seq); + + /** \brief Sets destination unreachable for this PDU. + */ + void set_dest_unreachable(); + + /** \brief Sets time exceeded flag for this PDU. + * + * \param ttl_exceeded If true this PDU will represent a ICMP ttl + * exceeded, otherwise it will represent a fragment reassembly + * time exceeded. + */ + void set_time_exceeded(bool ttl_exceeded = true); + + /** \brief Sets parameter problem flag for this PDU. + * + * \param set_pointer Indicates wether a pointer to the bad octet + * is provided. + * \param bad_octet Identifies the octet in which the error was + * detected. If set_pointer == false, it is ignored. + */ + void set_param_problem(bool set_pointer = false, uint8_t bad_octet = 0); + + /** \brief Sets source quench flag for this PDU. + */ + void set_source_quench(); + + /** \brief Sets redirect flag for this PDU. + * + * \param icode The code to be set. + * \param address Address of the gateway to which traffic should + * be sent. + */ + void set_redirect(uint8_t icode, uint32_t address); + + /** \brief Returns the ICMP type flag. + */ + Flags type() const { return (Flags)_icmp.type; } + + /** \brief Returns the ICMP code flag. + */ + uint8_t code() const { return _icmp.code; } + + /** \brief Returns the header size. + * + * This metod overrides PDU::header_size. This size includes the + * payload and options size. \sa PDU::header_size + */ + uint32_t header_size() const; + private: + static uint16_t global_id, global_seq; + + struct icmphdr { + uint8_t type; + uint8_t code; + uint16_t check; + union { + struct { + uint16_t id; + uint16_t sequence; + } echo; + uint32_t gateway; + struct { + uint16_t __unused; + uint16_t mtu; + } frag; + } un; + } __attribute__((packed)); + + /** \brief Serialices this ICMP 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, const PDU *parent); + + icmphdr _icmp; + }; +}; + +#endif diff --git a/include/tcp.h b/include/tcp.h index b8e6f36..c9e331a 100644 --- a/include/tcp.h +++ b/include/tcp.h @@ -182,7 +182,6 @@ namespace Tins { */ void add_option(Options tcp_option, uint8_t length = 0, uint8_t *data = 0); - /* Virtual methods */ /** \brief Returns the header size. * * This metod overrides PDU::header_size. This size includes the diff --git a/include/udp.h b/include/udp.h index 03ef6f0..74c0709 100644 --- a/include/udp.h +++ b/include/udp.h @@ -76,7 +76,6 @@ namespace Tins { */ void payload(uint8_t *new_payload, uint32_t new_payload_size); - /* Virtual methods */ /** \brief Returns the header size. * * This metod overrides PDU::header_size. This size includes the diff --git a/src/icmp.cpp b/src/icmp.cpp new file mode 100644 index 0000000..f117292 --- /dev/null +++ b/src/icmp.cpp @@ -0,0 +1,124 @@ +#ifndef WIN32 + #include +#endif +#include +#include +#include "icmp.h" +#include "utils.h" + + +uint16_t Tins::ICMP::global_id = 0, Tins::ICMP::global_seq = 0; + + +Tins::ICMP::ICMP(Flags flag) : PDU(IPPROTO_ICMP) { + std::memset(&_icmp, 0, sizeof(icmphdr)); + switch(flag) { + case ECHO_REPLY: + break; + case ECHO_REQUEST: + set_echo_request(); + break; + case DEST_UNREACHABLE: + set_dest_unreachable(); + break; + default: + break; + }; +} + +void Tins::ICMP::code(uint8_t new_code) { + _icmp.code = new_code; +} + +void Tins::ICMP::type(uint8_t new_type) { + _icmp.type = new_type; +} + +uint32_t Tins::ICMP::header_size() const { + return sizeof(icmphdr); +} + +void Tins::ICMP::set_echo_request(uint16_t id, uint16_t seq) { + _icmp.type = ECHO_REQUEST; + _icmp.un.echo.id = Utils::net_to_host_s(id); + _icmp.un.echo.sequence = Utils::net_to_host_s(seq); +} + +void Tins::ICMP::set_echo_request() { + set_echo_request(global_id++, global_seq++); + if(global_id == 0xffff) + global_id = 0; + if(global_seq == 0xffff) + global_seq = 0; +} + +void Tins::ICMP::set_echo_reply(uint16_t id, uint16_t seq) { + _icmp.type = ECHO_REPLY; + _icmp.un.echo.id = Utils::net_to_host_s(id); + _icmp.un.echo.sequence = Utils::net_to_host_s(seq); +} + +void Tins::ICMP::set_echo_reply() { + set_echo_reply(global_id++, global_seq++); + if(global_id == 0xffff) + global_id = 0; + if(global_seq == 0xffff) + global_seq = 0; +} + +void Tins::ICMP::set_info_request(uint16_t id, uint16_t seq) { + _icmp.type = INFO_REQUEST; + _icmp.code = 0; + _icmp.un.echo.id = Utils::net_to_host_s(id); + _icmp.un.echo.sequence = Utils::net_to_host_s(seq); +} + +void Tins::ICMP::set_info_reply(uint16_t id, uint16_t seq) { + _icmp.type = INFO_REPLY; + _icmp.code = 0; + _icmp.un.echo.id = Utils::net_to_host_s(id); + _icmp.un.echo.sequence = Utils::net_to_host_s(seq); +} + +void Tins::ICMP::set_dest_unreachable() { + _icmp.type = DEST_UNREACHABLE; +} + +void Tins::ICMP::set_time_exceeded(bool ttl_exceeded) { + _icmp.type = TIME_EXCEEDED; + _icmp.code = (ttl_exceeded) ? 0 : 1; +} + +void Tins::ICMP::set_param_problem(bool set_pointer, uint8_t bad_octet) { + _icmp.type = PARAM_PROBLEM; + if(set_pointer) { + _icmp.code = 0; + _icmp.un.echo.id = bad_octet; + } + else + _icmp.code = 1; +} + +void Tins::ICMP::set_source_quench() { + _icmp.type = SOURCE_QUENCH; +} + +void Tins::ICMP::set_redirect(uint8_t icode, uint32_t address) { + _icmp.type = REDIRECT; + _icmp.code = icode; + _icmp.un.gateway = address; +} + +void Tins::ICMP::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *) { + assert(total_sz >= sizeof(icmphdr)); + if(!_icmp.check) { + uint32_t checksum = PDU::do_checksum(buffer + sizeof(icmphdr), buffer + total_sz) + PDU::do_checksum((uint8_t*)&_icmp, ((uint8_t*)&_icmp) + sizeof(icmphdr)); + while (checksum >> 16) + checksum = (checksum & 0xffff) + (checksum >> 16); + _icmp.check = Utils::net_to_host_s(~checksum); + } + memcpy(buffer, &_icmp, sizeof(icmphdr)); + _icmp.check = 0; +} + + diff --git a/src/packetsender.cpp b/src/packetsender.cpp index b6c4dc0..43dc7c5 100644 --- a/src/packetsender.cpp +++ b/src/packetsender.cpp @@ -91,7 +91,6 @@ 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/tcp.cpp b/src/tcp.cpp index d806128..a4d508a 100644 --- a/src/tcp.cpp +++ b/src/tcp.cpp @@ -155,6 +155,7 @@ void Tins::TCP::write_serialization(uint8_t *buffer, uint32_t total_sz, const PD _tcp.check = Utils::net_to_host_s(~checksum); } memcpy(tcp_start, &_tcp, sizeof(tcphdr)); + _tcp.check = 0; } diff --git a/src/udp.cpp b/src/udp.cpp index 0beb845..8df7962 100644 --- a/src/udp.cpp +++ b/src/udp.cpp @@ -64,5 +64,6 @@ void Tins::UDP::write_serialization(uint8_t *buffer, uint32_t total_sz, const PD _udp.check = Utils::net_to_host_s(~checksum); } std::memcpy(buffer, &_udp, sizeof(udphdr)); + _udp.check = 0; }