diff --git a/Makefile.am b/Makefile.am index 09f7c20..c72d57a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -110,7 +110,8 @@ libtins_HEADERS = include/internals.h \ include/stp.h \ include/exceptions.h \ include/config.h \ - include/address_range.h + include/address_range.h \ + include/pdu_allocator.h libtins_dot11_HEADERS = include/dot11/dot11_base.h \ include/dot11/dot11_beacon.h \ diff --git a/Makefile.in b/Makefile.in index 0ce37f9..f561bd4 100644 --- a/Makefile.in +++ b/Makefile.in @@ -397,7 +397,8 @@ libtins_HEADERS = include/internals.h \ include/stp.h \ include/exceptions.h \ include/config.h \ - include/address_range.h + include/address_range.h \ + include/pdu_allocator.h libtins_dot11_HEADERS = include/dot11/dot11_base.h \ include/dot11/dot11_beacon.h \ diff --git a/include/loopback.h b/include/loopback.h index a6b964f..6db07f7 100644 --- a/include/loopback.h +++ b/include/loopback.h @@ -84,7 +84,7 @@ public: * \brief Getter for the PDU's type. * \sa PDU::pdu_type */ - PDUType pdu_type() const { return PDU::IP; } + PDUType pdu_type() const { return pdu_flag; } /** * \sa PDU::clone diff --git a/include/pdu_allocator.h b/include/pdu_allocator.h new file mode 100644 index 0000000..7ba1904 --- /dev/null +++ b/include/pdu_allocator.h @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2012, Matias Fontanini + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef TINS_PDU_ALLOCATOR_H +#define TINS_PDU_ALLOCATOR_H + +#include +#include "pdu.h" + +namespace Tins { +/** + * \cond + */ +class EthernetII; +class SNAP; +class Dot1Q; +class SLL; +class IP; +class IPv6; + +namespace Internals { +template +PDU *default_allocator(const uint8_t *buffer, uint32_t size) +{ + return new PDUType(buffer, size); +} + +template +class PDUAllocator { +public: + typedef typename Tag::identifier_type id_type; + typedef PDU *(*allocator_type)(const uint8_t *, uint32_t); + + template + static void register_allocator(id_type identifier) + { + allocators[identifier] = &default_allocator; + pdu_types[PDUType::pdu_flag] = identifier; + } + + static PDU *allocate(id_type identifier, const uint8_t *buffer, uint32_t size) + { + typename allocators_type::const_iterator it = allocators.find(identifier); + return (it == allocators.end()) ? 0 : (*it->second)(buffer, size); + } + + static bool pdu_type_registered(PDU::PDUType type) + { + return pdu_types.count(type) != 0; + } + + static id_type pdu_type_to_id(PDU::PDUType type) + { + typename pdu_map_types::const_iterator it = pdu_types.find(type); + return it->second; + } +private: + typedef std::map allocators_type; + typedef std::map pdu_map_types; + + static allocators_type allocators; + static pdu_map_types pdu_types; +}; + +template +typename PDUAllocator::allocators_type PDUAllocator::allocators; + +template +typename PDUAllocator::pdu_map_types PDUAllocator::pdu_types; + +template +struct pdu_tag { + typedef IDType identifier_type; +}; + +template +struct pdu_tag_mapper; + +#define TINS_GENERATE_TAG_MAPPER(pdu, id_type) \ +template<> \ +struct pdu_tag_mapper { \ + typedef pdu_tag type; \ +}; + +TINS_GENERATE_TAG_MAPPER(EthernetII, uint16_t) +TINS_GENERATE_TAG_MAPPER(SNAP, uint16_t) +TINS_GENERATE_TAG_MAPPER(SLL, uint16_t) +TINS_GENERATE_TAG_MAPPER(Dot1Q, uint16_t) +TINS_GENERATE_TAG_MAPPER(IP, uint8_t) +TINS_GENERATE_TAG_MAPPER(IPv6, uint8_t) + +#undef TINS_GENERATE_TAG_MAPPER + +template +PDU* allocate( + typename pdu_tag_mapper::type::identifier_type id, + const uint8_t *buffer, + uint32_t size) +{ + return PDUAllocator::type>::allocate(id, buffer, size); +} + +template +bool pdu_type_registered(PDU::PDUType type) +{ + return PDUAllocator::type>::pdu_type_registered(type); +} + +template +typename pdu_tag_mapper::type::identifier_type pdu_type_to_id(PDU::PDUType type) +{ + return PDUAllocator::type>::pdu_type_to_id(type); +} +} // namespace Interals +/** + * \endcond + */ + +/** + * \brief Defines inner PDU allocators. + */ +namespace Allocators { +/** + * \brief Registers an allocator for the provided PDU type. + * + * Registering a certain allocator for a PDU type is useful for + * extending the library. Once an allocator is registered, it will + * be taken into account while constructing a PDU from a buffer. + * + * If PDU finds that it cannot define which is the protocol + * that should be allocated based on its protocol identifier, it + * will try using the registered allocators if any. + * + * \code + * // Register the 0x666 identifer. Now if EthernetII finds a + * // network layer identifier field whose value is 0x666, it will + * // use SomePDUType as its inner PDU type. + * Allocators::register_allocator(0x666); + * \endcode + * + * Note that some PDU types are grouped together. For example, + * registering an allocator for EthernetII will make it work for + * the rest of the link layer protocols, sine they should all work + * the same way. + */ +template +void register_allocator(typename Internals::pdu_tag_mapper::type::identifier_type id) +{ + Internals::PDUAllocator< + typename Internals::pdu_tag_mapper::type + >::template register_allocator(id); +} +} // namespace Allocators +} // namespace Tins + +#endif // TINS_PDU_ALLOCATOR_H diff --git a/include/tins.h b/include/tins.h index 1d07e92..dbc3d32 100644 --- a/include/tins.h +++ b/include/tins.h @@ -69,5 +69,6 @@ #include "stp.h" #include "handshake_capturer.h" #include "address_range.h" +#include "pdu_allocator.h" #endif // TINS_TINS_H diff --git a/src/internals.cpp b/src/internals.cpp index 8878ed8..1b60cd1 100644 --- a/src/internals.cpp +++ b/src/internals.cpp @@ -41,6 +41,7 @@ #include "pppoe.h" #include "ip_address.h" #include "ipv6_address.h" +#include "pdu_allocator.h" using std::string; @@ -74,18 +75,27 @@ Tins::PDU *pdu_from_flag(Constants::Ethernet::e flag, const uint8_t *buffer, { switch(flag) { case Tins::Constants::Ethernet::IP: - return new Tins::IP(buffer, size); + return new IP(buffer, size); case Constants::Ethernet::IPV6: - return new Tins::IPv6(buffer, size); + return new IPv6(buffer, size); case Tins::Constants::Ethernet::ARP: - return new Tins::ARP(buffer, size); + return new ARP(buffer, size); case Tins::Constants::Ethernet::PPPOED: - return new Tins::PPPoE(buffer, size); + return new PPPoE(buffer, size); case Tins::Constants::Ethernet::EAPOL: - return Tins::EAPOL::from_bytes(buffer, size); + return EAPOL::from_bytes(buffer, size); case Tins::Constants::Ethernet::VLAN: - return new Tins::Dot1Q(buffer, size); + return new Dot1Q(buffer, size); default: + { + PDU *pdu = Internals::allocate( + static_cast(flag), + buffer, + size + ); + if(pdu) + return pdu; + } return rawpdu_on_no_match ? new RawPDU(buffer, size) : 0; }; } @@ -148,6 +158,10 @@ Constants::Ethernet::e pdu_flag_to_ether_type(PDU::PDUType flag) { case PDU::PPPOE: return Constants::Ethernet::PPPOED; default: + if(Internals::pdu_type_registered(flag)) + return static_cast( + Internals::pdu_type_to_id(flag) + ); return Constants::Ethernet::UNKNOWN; } } diff --git a/src/ip.cpp b/src/ip.cpp index e0acf09..54cb2cb 100644 --- a/src/ip.cpp +++ b/src/ip.cpp @@ -52,6 +52,7 @@ #include "constants.h" #include "network_interface.h" #include "exceptions.h" +#include "pdu_allocator.h" using std::list; @@ -134,7 +135,15 @@ IP::IP(const uint8_t *buffer, uint32_t total_sz) inner_pdu(new Tins::IPv6(buffer, total_sz)); break; default: - inner_pdu(new Tins::RawPDU(buffer, total_sz)); + inner_pdu( + Internals::allocate( + _ip.protocol, + buffer, + total_sz + ) + ); + if(!inner_pdu()) + inner_pdu(new RawPDU(buffer, total_sz)); break; } } @@ -397,8 +406,13 @@ void IP::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU* pare new_flag = Constants::IP::PROTO_ICMP; break; default: - // check for other protos - new_flag = 0xff; + if(Internals::pdu_type_registered(inner_pdu()->pdu_type())) + new_flag = static_cast( + Internals::pdu_type_to_id(inner_pdu()->pdu_type()) + ); + else + // check for other protos + new_flag = 0xff; }; protocol(new_flag); //flag(new_flag); diff --git a/src/ipv6.cpp b/src/ipv6.cpp index 983f6db..490fd35 100644 --- a/src/ipv6.cpp +++ b/src/ipv6.cpp @@ -48,6 +48,7 @@ #include "icmpv6.h" #include "rawpdu.h" #include "exceptions.h" +#include "pdu_allocator.h" namespace Tins { @@ -101,7 +102,15 @@ IPv6::IPv6(const uint8_t *buffer, uint32_t total_sz) inner_pdu(new Tins::ICMPv6(buffer, total_sz)); break; default: - inner_pdu(new Tins::RawPDU(buffer, total_sz)); + inner_pdu( + Internals::allocate( + current_header, + buffer, + total_sz + ) + ); + if(!inner_pdu()) + inner_pdu(new Tins::RawPDU(buffer, total_sz)); break; } total_sz = 0; @@ -214,10 +223,13 @@ void IPv6::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *pa new_flag = Constants::IP::PROTO_ICMPV6; break; default: + if(Internals::pdu_type_registered(inner_pdu()->pdu_type())) + new_flag = static_cast( + Internals::pdu_type_to_id(inner_pdu()->pdu_type()) + ); break; }; - if(new_flag != 0xff) - set_last_next_header(new_flag); + set_last_next_header(new_flag); } payload_length(total_sz - sizeof(_header)); std::memcpy(buffer, &_header, sizeof(_header)); diff --git a/tests/depends.d b/tests/depends.d index 744a706..f2fa067 100644 --- a/tests/depends.d +++ b/tests/depends.d @@ -874,7 +874,8 @@ ../include/dot11/../cxxstd.h ../include/dot11/../macros.h \ ../include/ipv6.h ../include/ipv6_address.h ../include/arp.h \ ../include/eapol.h ../include/rawpdu.h ../include/dot1q.h \ - ../include/pppoe.h ../include/ip_address.h ../include/ipv6_address.h + ../include/pppoe.h ../include/ip_address.h ../include/ipv6_address.h \ + ../include/pdu_allocator.h ../include/internals.h: @@ -941,6 +942,8 @@ ../include/ip_address.h: ../include/ipv6_address.h: + +../include/pdu_allocator.h: ../src/ip.o: ../src/ip.cpp ../include/ip.h ../include/pdu.h \ ../include/macros.h ../include/cxxstd.h ../include/exceptions.h \ ../include/small_uint.h ../include/endianness.h ../include/ip_address.h \ @@ -949,7 +952,8 @@ ../include/utils.h ../include/hw_address.h ../include/internals.h \ ../include/constants.h ../include/packet_sender.h \ ../include/network_interface.h ../include/constants.h \ - ../include/network_interface.h ../include/exceptions.h + ../include/network_interface.h ../include/exceptions.h \ + ../include/pdu_allocator.h ../include/ip.h: @@ -998,6 +1002,8 @@ ../include/network_interface.h: ../include/exceptions.h: + +../include/pdu_allocator.h: ../src/ip_address.o: ../src/ip_address.cpp ../include/ip_address.h \ ../include/cxxstd.h ../include/endianness.h ../include/macros.h \ ../include/address_range.h ../include/ip_address.h \ @@ -1034,7 +1040,7 @@ ../include/packet_sender.h ../include/network_interface.h \ ../include/hw_address.h ../include/ip_address.h ../include/ip.h \ ../include/tcp.h ../include/udp.h ../include/icmp.h ../include/icmpv6.h \ - ../include/rawpdu.h ../include/exceptions.h + ../include/rawpdu.h ../include/exceptions.h ../include/pdu_allocator.h ../include/ipv6.h: @@ -1077,14 +1083,37 @@ ../include/rawpdu.h: ../include/exceptions.h: + +../include/pdu_allocator.h: ../src/ipv6_address.o: ../src/ipv6_address.cpp ../include/macros.h \ - ../include/ipv6_address.h ../include/cxxstd.h + ../include/ipv6_address.h ../include/cxxstd.h ../include/address_range.h \ + ../include/ip_address.h ../include/ipv6_address.h \ + ../include/endianness.h ../include/macros.h ../include/internals.h \ + ../include/constants.h ../include/pdu.h ../include/exceptions.h ../include/macros.h: ../include/ipv6_address.h: ../include/cxxstd.h: + +../include/address_range.h: + +../include/ip_address.h: + +../include/ipv6_address.h: + +../include/endianness.h: + +../include/macros.h: + +../include/internals.h: + +../include/constants.h: + +../include/pdu.h: + +../include/exceptions.h: ../src/llc.o: ../src/llc.cpp ../include/llc.h ../include/macros.h \ ../include/pdu.h ../include/cxxstd.h ../include/exceptions.h \ ../include/endianness.h ../include/stp.h ../include/hw_address.h \ @@ -1800,6 +1829,47 @@ src/address_range.o: src/address_range.cpp ../include/address_range.h \ ../include/ip_address.h: +../include/ipv6_address.h: +src/allocators.o: src/allocators.cpp ../include/pdu_allocator.h \ + ../include/pdu.h ../include/macros.h ../include/cxxstd.h \ + ../include/exceptions.h ../include/ethernetII.h ../include/endianness.h \ + ../include/hw_address.h ../include/snap.h ../include/small_uint.h \ + ../include/sll.h ../include/dot1q.h ../include/ip.h \ + ../include/ip_address.h ../include/pdu_option.h ../include/ipv6.h \ + ../include/ipv6_address.h + +../include/pdu_allocator.h: + +../include/pdu.h: + +../include/macros.h: + +../include/cxxstd.h: + +../include/exceptions.h: + +../include/ethernetII.h: + +../include/endianness.h: + +../include/hw_address.h: + +../include/snap.h: + +../include/small_uint.h: + +../include/sll.h: + +../include/dot1q.h: + +../include/ip.h: + +../include/ip_address.h: + +../include/pdu_option.h: + +../include/ipv6.h: + ../include/ipv6_address.h: src/arp.o: src/arp.cpp ../include/arp.h ../include/macros.h \ ../include/pdu.h ../include/cxxstd.h ../include/exceptions.h \ @@ -2778,7 +2848,7 @@ src/ethernetII.o: src/ethernetII.cpp ../include/ethernetII.h \ ../include/utils.h ../include/ip_address.h ../include/ipv6_address.h \ ../include/internals.h ../include/constants.h ../include/macros.h \ ../include/ipv6.h ../include/small_uint.h ../include/pdu_option.h \ - ../include/ip.h + ../include/ip.h ../include/tcp.h ../include/rawpdu.h ../include/ethernetII.h: @@ -2813,6 +2883,10 @@ src/ethernetII.o: src/ethernetII.cpp ../include/ethernetII.h \ ../include/pdu_option.h: ../include/ip.h: + +../include/tcp.h: + +../include/rawpdu.h: src/hwaddress.o: src/hwaddress.cpp ../include/hw_address.h \ ../include/cxxstd.h diff --git a/tests/src/allocators.cpp b/tests/src/allocators.cpp new file mode 100644 index 0000000..fd93124 --- /dev/null +++ b/tests/src/allocators.cpp @@ -0,0 +1,119 @@ +#include +#include +#include +#include +#include "pdu_allocator.h" +#include "ethernetII.h" +#include "snap.h" +#include "sll.h" +#include "dot1q.h" +#include "ip.h" +#include "ipv6.h" + + +using namespace Tins; + +class AllocatorsTest : public testing::Test { +public: + static const uint8_t link_layer_data_buffer[], ipv4_data_buffer[], ipv6_data_buffer[]; +}; + +const uint8_t AllocatorsTest::link_layer_data_buffer[] = { + 0, 27, 17, 210, 243, 22, 0, 25, 209, 22, 248, 43, 6, 102, 65, 65, 65, + 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, + 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, + 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, + 65, 65, 65, 65, 65, 65 +}; + +const uint8_t AllocatorsTest::ipv4_data_buffer[] = { + 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 8, 0, 69, 0, 0, 60, + 0, 1, 0, 0, 64, 255, 123, 192, 127, 0, 0, 1, 127, 0, 0, 1, 65, 65, + 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, + 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, + 65, 65, 65, 65 +}; + +const uint8_t AllocatorsTest::ipv6_data_buffer[] = { + 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 134, 221, 96, 0, 0, + 0, 0, 40, 250, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 65, 65, 65, 65, 65, + 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, + 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, + 65 +}; + +class DummyPDU : public PDU { +public: + static const PDU::PDUType pdu_flag; + + DummyPDU(const uint8_t* data, uint32_t sz) : buffer(data, data + sz) { } + DummyPDU *clone() const { return new DummyPDU(*this); } + uint32_t header_size() const { return buffer.size(); } + PDUType pdu_type() const { return pdu_flag; } + void write_serialization(uint8_t *data, uint32_t, const PDU *) + { + std::copy(buffer.begin(), buffer.end(), data); + } + + std::vector buffer; +}; + +const PDU::PDUType DummyPDU::pdu_flag = static_cast(0xefff); + +TEST_F(AllocatorsTest, LinkLayerPDUs) { + Allocators::register_allocator(1638); + Allocators::register_allocator(25); + Allocators::register_allocator(4562); + Allocators::register_allocator(16705); + std::vector link_layer_data( + link_layer_data_buffer, + link_layer_data_buffer + sizeof(link_layer_data_buffer) + ); + { + EthernetII pkt(&link_layer_data[0], link_layer_data.size()); + EXPECT_TRUE(pkt.find_pdu()); + EXPECT_EQ(pkt.serialize(), link_layer_data); + } + { + SNAP pkt(&link_layer_data[0], link_layer_data.size()); + EXPECT_TRUE(pkt.find_pdu()); + EXPECT_EQ(pkt.serialize(), link_layer_data); + } + { + SLL pkt(&link_layer_data[0], link_layer_data.size()); + EXPECT_TRUE(pkt.find_pdu()); + EXPECT_EQ(pkt.serialize(), link_layer_data); + } + { + Dot1Q pkt(&link_layer_data[0], link_layer_data.size()); + EXPECT_TRUE(pkt.find_pdu()); + EXPECT_EQ(pkt.serialize(), link_layer_data); + } +} + +TEST_F(AllocatorsTest, IP) { + std::vector ipv4_data( + ipv4_data_buffer, + ipv4_data_buffer + sizeof(ipv4_data_buffer) + ); + Allocators::register_allocator(255); + EthernetII pkt(&ipv4_data[0], ipv4_data.size()); + EXPECT_TRUE(pkt.find_pdu()); + EXPECT_TRUE(pkt.find_pdu()); + EXPECT_EQ(pkt.serialize(), ipv4_data); +} + +TEST_F(AllocatorsTest, IPv6) { + std::vector ipv6_data( + ipv6_data_buffer, + ipv6_data_buffer + sizeof(ipv6_data_buffer) + ); + Allocators::register_allocator(250); + { + EthernetII pkt(&ipv6_data[0], ipv6_data.size()); + EXPECT_TRUE(pkt.find_pdu()); + EXPECT_TRUE(pkt.find_pdu()); + EXPECT_EQ(pkt.serialize(), ipv6_data); + } +}