From b76c9d05253bd8e964db2d541717540834853ffc Mon Sep 17 00:00:00 2001 From: Santiago Alessandri Date: Mon, 15 Aug 2011 18:16:48 -0300 Subject: [PATCH] Working ethernet II PDU. Fixed bugs in IP and added checksum calculation. Added layer2 sending to PacketSender --- include/ethernet.h | 57 +++++++++++++++---------- include/icmp.h | 94 +++++++++++++++++++++--------------------- include/packetsender.h | 23 +++++++---- src/ethernet.cpp | 59 ++++++++++++++++++-------- src/icmp.cpp | 3 +- src/ip.cpp | 12 +++++- src/packetsender.cpp | 28 +++++++++---- src/pdu.cpp | 18 ++++---- src/utils.cpp | 34 +++++---------- 9 files changed, 192 insertions(+), 136 deletions(-) diff --git a/include/ethernet.h b/include/ethernet.h index e6da20d..2eaa986 100644 --- a/include/ethernet.h +++ b/include/ethernet.h @@ -23,6 +23,7 @@ #define __ETHERNET_H #include +#include #include "pdu.h" @@ -42,8 +43,23 @@ namespace Tins { * * \param mac_dst uint8_t array of 6 bytes containing the destination's MAC. * \param mac_src uint8_t array of 6 bytes containing the source's MAC. + * \param iface string containing the interface's name from where to send the packet. + * \param child PDU* with the PDU contained by the ethernet PDU (optional). */ - Ethernet(const uint8_t mac_dst[6], const uint8_t mac_src[6], PDU* child = 0); + Ethernet(const uint8_t* mac_dst, const uint8_t* mac_src, const std::string& iface, PDU* child = 0) throw (std::runtime_error); + + /** + * \brief Constructor for creating an ethernet PDU + * + * Constructor that builds an ethernet PDU taking the destination's + * and source's MAC. + * + * \param mac_dst uint8_t array of 6 bytes containing the destination's MAC. + * \param mac_src uint8_t array of 6 bytes containing the source's MAC. + * \param iface_index uint32_t containing the interface's index from where to send the packet. + * \param child PDU* with the PDU contained by the ethernet PDU (optional). + */ + Ethernet(const uint8_t* mac_dst, const uint8_t* mac_src, const uint32_t iface_index, PDU* child = 0); /* Getters */ /** @@ -61,11 +77,11 @@ namespace Tins { inline const uint8_t* src_mac() const { return this->header.src_mac; } /** - * \brief Getter for the CRC value. + * \brief Getter for the interface. * - * \return Returns the CRC. + * \return Returns the interface's index as an uint32_t. */ - inline uint32_t crc() const { return this->_crc; } + inline uint32_t iface() const { return this->_iface_index; } /* Setters */ /** @@ -73,21 +89,28 @@ namespace Tins { * * \param new_dst_mac uint8_t array of 6 bytes containing the new destination's MAC. */ - void dst_mac(uint8_t new_dst_mac[6]); + void dst_mac(const uint8_t* new_dst_mac); /** * \brief Setter for the source's MAC. * * \param new_src_mac uint8_t array of 6 bytes containing the new source's MAC. */ - void src_mac(uint8_t new_src_mac[6]); + void src_mac(const uint8_t* new_src_mac); /** - * \brief Setter for the CRC value. + * \brief Setter for the interface. * - * \param new_crc uint32_t containing the new CRC value. + * \param new_iface_index uint32_t containing the new interface index. */ - void crc(uint32_t new_crc); + void iface(uint32_t new_iface_index); + + /** + * \brief Setter for the interface. + * + * \param new_iface string reference containing the new interface name. + */ + void iface(const std::string& new_iface) throw (std::runtime_error); /* Virtual methods */ /** @@ -98,14 +121,6 @@ namespace Tins { */ uint32_t header_size() const; - /** - * \brief Returns the ethernet frame's trailer length. - * - * \return An uint32_t with the trailer's size. - * \sa PDU::trailer_size() - */ - uint32_t trailer_size() const; - /** * \sa PDU::send() */ @@ -113,18 +128,18 @@ namespace Tins { private: /** - * Struct that represents the IEEE 802.3 header + * Struct that represents the Ethernet II header */ struct ethernet_header { uint8_t dst_mac[6]; uint8_t src_mac[6]; uint16_t payload_type; - }; + } __attribute__((__packed__)); ethernet_header header; - uint32_t _crc; + uint32_t _iface_index; - void write_serialization(uint8_t *buffer, uint32_t total_sz, PDU *parent); + void write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *parent); }; diff --git a/include/icmp.h b/include/icmp.h index f64802d..c612f5c 100644 --- a/include/icmp.h +++ b/include/icmp.h @@ -5,16 +5,16 @@ #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, @@ -25,123 +25,123 @@ namespace Tins { 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); - + ICMP(Flags flag = ECHO_REQUEST); + /** \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. + * 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. + * 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 + * + * \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; - + bool matches_response(uint8_t *ptr, uint32_t total_sz); - + PDU *clone_packet(uint8_t *ptr, uint32_t total_sz); private: static uint16_t global_id, global_seq; - + struct icmphdr { uint8_t type; uint8_t code; @@ -157,21 +157,21 @@ namespace Tins { uint16_t mtu; } frag; } un; - } __attribute__((packed)); - + } __attribute__((__packed__)); + /** \brief Creates an instance of ICMP from a icmphdr pointer. - * + * * \param ptr The icmphdr to clone. */ ICMP(icmphdr *ptr); - + /** \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; }; }; diff --git a/include/packetsender.h b/include/packetsender.h index 9b3e366..45d52cb 100644 --- a/include/packetsender.h +++ b/include/packetsender.h @@ -26,6 +26,11 @@ #include #include #include + +#ifndef WIN32 + #include +#endif + #include "pdu.h" namespace Tins { @@ -40,17 +45,21 @@ namespace Tins { class PacketSender { public: enum SocketType { + ETHER_SOCKET, IP_SOCKET, ICMP_SOCKET, SOCKETS_END }; - + /** * \brief Constructor for PacketSender objects. */ PacketSender(); - + /** + * \brief + * + */ bool open_l2_socket(); bool open_l3_socket(SocketType type); @@ -58,19 +67,19 @@ namespace Tins { bool close_socket(uint32_t flag); bool send(PDU* pdu); - + PDU *send_recv(PDU *pdu); - bool send_l2(PDU *pdu); - + bool send_l2(PDU *pdu, struct sockaddr* link_addr, uint32_t len_link_addr); + PDU *recv_l3(PDU *pdu, struct sockaddr *link_addr, uint32_t len_link_addr, SocketType type); bool send_l3(PDU *pdu, struct sockaddr *link_addr, uint32_t len_link_addr, SocketType type); private: static const int INVALID_RAW_SOCKET; - + typedef std::map SocketTypeMap; - + int find_type(SocketType type); std::vector _sockets; diff --git a/src/ethernet.cpp b/src/ethernet.cpp index cba278c..006edbe 100644 --- a/src/ethernet.cpp +++ b/src/ethernet.cpp @@ -22,42 +22,69 @@ #include #include -#include +#ifndef WIN32 + #include + #include + #include +#endif #include "ethernet.h" #include "utils.h" -Tins::Ethernet::Ethernet(const uint8_t mac_dst[6], const uint8_t mac_src[6], PDU* child) : PDU(ETHERTYPE_IP, child) { +Tins::Ethernet::Ethernet(const uint8_t* mac_dst, const uint8_t* mac_src, const std::string& iface, PDU* child) throw (std::runtime_error) : PDU(ETHERTYPE_IP, child) { + + this->dst_mac(mac_dst); + this->src_mac(mac_src); + this->iface(iface); } -void Tins::Ethernet::dst_mac(uint8_t new_dst_mac[6]) { +Tins::Ethernet::Ethernet(const uint8_t* mac_dst, const uint8_t* mac_src, uint32_t iface_index, PDU* child) : PDU(ETHERTYPE_IP, child) { + this->dst_mac(mac_dst); + this->src_mac(mac_src); + this->iface(iface_index); +} + +void Tins::Ethernet::dst_mac(const uint8_t* new_dst_mac) { memcpy(this->header.dst_mac, new_dst_mac, 6); } -void Tins::Ethernet::src_mac(uint8_t new_src_mac[6]) { +void Tins::Ethernet::src_mac(const uint8_t* new_src_mac) { memcpy(this->header.src_mac, new_src_mac, 6); } -void Tins::Ethernet::crc(uint32_t new_crc) { - this->_crc = new_crc; +void Tins::Ethernet::iface(uint32_t new_iface_index) { + this->_iface_index = new_iface_index; +} + +void Tins::Ethernet::iface(const std::string& new_iface) throw (std::runtime_error) { + if (!Tins::Utils::interface_id(new_iface, this->_iface_index)) { + throw std::runtime_error("Invalid interface name!"); + } } uint32_t Tins::Ethernet::header_size() const { return sizeof(ethernet_header); } -uint32_t Tins::Ethernet::trailer_size() const { - return sizeof(uint32_t); -} - bool Tins::Ethernet::send(PacketSender* sender) { - return false; //return sender->send_l2(this); + + struct sockaddr_ll addr; + + memset(&addr, 0, sizeof(struct sockaddr_ll)); + + addr.sll_family = Utils::net_to_host_s(PF_PACKET); + addr.sll_protocol = Utils::net_to_host_s(ETH_P_ALL); + addr.sll_halen = 6; + addr.sll_ifindex = this->_iface_index; + memcpy(&(addr.sll_addr), this->header.dst_mac, 6); + + return sender->send_l2(this, (struct sockaddr*)&addr, (uint32_t)sizeof(addr)); + } -void Tins::Ethernet::write_serialization(uint8_t *buffer, uint32_t total_sz, PDU *parent) { - uint32_t my_sz = header_size() + trailer_size(); - uint32_t new_flag; +void Tins::Ethernet::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *parent) { + uint32_t my_sz = header_size(); assert(total_sz >= my_sz); /* if (this->inner_pdu()) { @@ -70,10 +97,8 @@ void Tins::Ethernet::write_serialization(uint8_t *buffer, uint32_t total_sz, PDU } */ /* This should be replaced by a switch statement */ - this->header.payload_type = ETHERTYPE_IP; - this->_crc = Tins::Utils::crc32(buffer, total_sz - sizeof(uint32_t)); + this->header.payload_type = Utils::net_to_host_s(ETHERTYPE_IP); memcpy(buffer, &this->header, sizeof(ethernet_header)); - *((uint32_t*)&buffer[total_sz - sizeof(uint32_t)]) = this->_crc; } diff --git a/src/icmp.cpp b/src/icmp.cpp index 88b78c0..1aeec42 100644 --- a/src/icmp.cpp +++ b/src/icmp.cpp @@ -7,7 +7,6 @@ #include "rawpdu.h" #include "utils.h" - uint16_t Tins::ICMP::global_id = 0, Tins::ICMP::global_seq = 0; @@ -149,7 +148,7 @@ Tins::PDU *Tins::ICMP::clone_packet(uint8_t *ptr, uint32_t total_sz) { } else child = new RawPDU(ptr + sizeof(icmphdr), total_sz - sizeof(icmphdr)); - + } cloned = new ICMP(icmp_ptr); cloned->inner_pdu(child); diff --git a/src/ip.cpp b/src/ip.cpp index b67c8e1..ae91e67 100644 --- a/src/ip.cpp +++ b/src/ip.cpp @@ -185,7 +185,7 @@ Tins::PDU *Tins::IP::recv_response(PacketSender *sender) { return sender->recv_l3(this, (struct sockaddr*)&link_addr, sizeof(link_addr), type); } -void Tins::IP::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *) { +void Tins::IP::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU* parent) { uint32_t my_sz = header_size(); uint32_t new_flag; assert(total_sz >= my_sz); @@ -198,8 +198,16 @@ void Tins::IP::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU new_flag = IPPROTO_IP; flag(new_flag); _ip.protocol = new_flag; - _ip.tot_len = total_sz; + _ip.tot_len = Utils::net_to_host_s(total_sz); _ip.ihl = my_sz / sizeof(uint32_t); + if (parent && (_ip.check == 0)) { + uint32_t checksum = PDU::do_checksum((uint8_t*)&_ip, ((uint8_t*)&_ip) + sizeof(iphdr)); + checksum += PDU::do_checksum(buffer + sizeof(iphdr), buffer + total_sz); + while (checksum >> 16) + checksum = (checksum & 0xffff) + (checksum >> 16); + _ip.check = Utils::net_to_host_s(~checksum); + } + memcpy(buffer, &_ip, sizeof(iphdr)); /* IP Options here... */ diff --git a/src/packetsender.cpp b/src/packetsender.cpp index eff7852..efb1e30 100644 --- a/src/packetsender.cpp +++ b/src/packetsender.cpp @@ -45,9 +45,16 @@ Tins::PacketSender::PacketSender() : _sockets(SOCKETS_END, INVALID_RAW_SOCKET) { bool Tins::PacketSender::open_l2_socket() { - /* To be implemented */ + if (_sockets[ETHER_SOCKET] != INVALID_RAW_SOCKET) + return true; - return false; + int sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); + if (sock == -1) + return false; + + _sockets[ETHER_SOCKET] = sock; + + return true; } bool Tins::PacketSender::open_l3_socket(SocketType type) { @@ -86,21 +93,28 @@ Tins::PDU *Tins::PacketSender::send_recv(PDU *pdu) { return pdu->recv_response(this); } -bool Tins::PacketSender::send_l2(PDU *pdu) { +bool Tins::PacketSender::send_l2(PDU *pdu, struct sockaddr* link_addr, uint32_t len_link_addr) { - /* To be implemented */ + if(!open_l2_socket()) + return false; - return false; + uint32_t sz; + int sock = _sockets[ETHER_SOCKET]; + uint8_t *buffer = pdu->serialize(sz); + bool ret_val = (sendto(sock, buffer, sz, 0, link_addr, len_link_addr) != -1); + delete[] buffer; + + return ret_val; } -Tins::PDU *Tins::PacketSender::recv_l3(PDU *pdu, struct sockaddr *link_addr, uint32_t len_link_addr, SocketType type) { +Tins::PDU *Tins::PacketSender::recv_l3(PDU *pdu, struct sockaddr* link_addr, uint32_t len_link_addr, SocketType type) { if(!open_l3_socket(type)) return 0; uint8_t buffer[2048]; int sock = _sockets[type]; bool done = false; socklen_t addrlen = len_link_addr; - + while(!done) { ssize_t size = recvfrom(sock, buffer, 2048, 0, link_addr, &addrlen); if(size == -1) diff --git a/src/pdu.cpp b/src/pdu.cpp index e1accf0..8dc27da 100644 --- a/src/pdu.cpp +++ b/src/pdu.cpp @@ -1,19 +1,19 @@ /* - * libtins is a net packet wrapper library for crafting and + * libtins is a net packet wrapper library for crafting and * interpreting sniffed packets. - * + * * Copyright (C) 2011 Nasel - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA @@ -25,7 +25,7 @@ Tins::PDU::PDU(uint32_t flag, PDU *next_pdu) : _flag(flag), _inner_pdu(next_pdu) { - + } Tins::PDU::~PDU() { @@ -85,10 +85,10 @@ uint32_t Tins::PDU::pseudoheader_checksum(uint32_t source_ip, uint32_t dest_ip, 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]; + + checksum += (uint32_t)(*ptr) + (uint32_t)(*(ptr+1)); ptr = (uint16_t*)&dest_ip; - checksum += *ptr + ptr[1]; + checksum += (uint32_t)(*ptr) + (uint32_t)(*(ptr+1)); checksum += flag + len; return checksum; } diff --git a/src/utils.cpp b/src/utils.cpp index c0f8de6..566131c 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -27,6 +27,7 @@ #ifndef WIN32 #include #include + #include #endif #include "utils.h" @@ -36,7 +37,7 @@ using namespace std; struct InterfaceCollector { set ifaces; - + void operator() (struct ifaddrs *addr) { ifaces.insert(addr->ifa_name); } @@ -46,9 +47,9 @@ struct IPv4Collector { uint32_t ip; bool found; const char *iface; - + IPv4Collector(const char *interface) : ip(0), found(false), iface(interface) { } - + void operator() (struct ifaddrs *addr) { if(!found && addr->ifa_addr->sa_family == AF_INET && !strcmp(addr->ifa_name, iface)) { ip = ((struct sockaddr_in *)addr->ifa_addr)->sin_addr.s_addr; @@ -57,28 +58,13 @@ struct IPv4Collector { } }; -struct InterfaceIDCollector { - uint32_t id; - bool found; - const char *iface; - - InterfaceIDCollector(const char *interface) : id(0), found(false), iface(interface) { } - - void operator() (struct ifaddrs *addr) { - if(!found && !strcmp(addr->ifa_name, iface)) { - id = addr->ifa_flags; - found = true; - } - } -}; - struct HWAddressCollector { uint8_t *result; bool found; const char *iface; - + HWAddressCollector(uint8_t *res, const char *interface) : result(res), found(false), iface(interface) { } - + void operator() (struct ifaddrs *addr) { if(!found && addr->ifa_addr->sa_family == AF_PACKET && !strcmp(addr->ifa_name, iface)) { memcpy(result, ((struct sockaddr_ll*)addr->ifa_addr)->sll_addr, 6); @@ -189,10 +175,10 @@ bool Tins::Utils::interface_hwaddr(const string &iface, uint8_t *buffer) { } bool Tins::Utils::interface_id(const string &iface, uint32_t &id) { - InterfaceIDCollector collector(iface.c_str()); - generic_iface_loop(collector); - id = collector.id; - return collector.found; + + id = if_nametoindex(iface.c_str()); + return (((int32_t)id) != -1); + } uint32_t Tins::Utils::crc32(uint8_t* data, uint32_t data_size) {