diff --git a/include/tins/icmp_extension.h b/include/tins/icmp_extension.h new file mode 100644 index 0000000..e4a5c75 --- /dev/null +++ b/include/tins/icmp_extension.h @@ -0,0 +1,78 @@ +#ifndef TINS_ICMP_EXTENSION_H +#define TINS_ICMP_EXTENSION_H + +#include +#include + +namespace Tins { + +/** + * \brief Class that represents an ICMP extension object + */ +class ICMPExtension { +public: + typedef std::vector payload_type; + typedef std::vector serialization_type; + + /** + * \brief Constructs an ICMP extension from a buffer + * + * \param buffer The input buffer + * \param total_sz The input buffer size + */ + ICMPExtension(const uint8_t* buffer, uint32_t total_sz); + + /** + * \brief Getter for the extension class field + * + * \return The extension class field value + */ + uint8_t extension_class() const { return extension_class_; } + + /** + * \brief Getter for the extension sub-type field + * + * \return The extension sub-type field value + */ + uint8_t extension_type() const { return extension_type_; } + + /** + * \brief Getter for the extension payload field + * + * \return The extension payload field value + */ + const payload_type& payload() const { return payload_; } + + /** + * \brief Gets the size of this ICMP extension + * + * This returns the basic header size + the payload size + * + * \return The size of this extension + */ + uint32_t extension_size() const; + + /** + * \brief Serializes this extension into a buffer + * + * \param buffer The output buffer in which to store the serialization + * \param buffer_size The size of the output buffer + */ + void serialize(uint8_t* buffer, uint32_t buffer_size) const; + + /** + * \brief Serializes this ICMP extension object + * + * \return The serialized ICMP extension + */ + serialization_type serialize() const; +private: + static const uint32_t BASE_HEADER_SIZE; + + payload_type payload_; + uint8_t extension_class_, extension_type_; +}; + +} // Tins + +#endif // TINS_ICMP_EXTENSION_H diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 85af67e..a32d710 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -25,6 +25,7 @@ ADD_LIBRARY( dot1q.cpp eapol.cpp ethernetII.cpp + icmp_extension.cpp icmp.cpp icmpv6.cpp internals.cpp diff --git a/src/icmp_extension.cpp b/src/icmp_extension.cpp new file mode 100644 index 0000000..5cfa895 --- /dev/null +++ b/src/icmp_extension.cpp @@ -0,0 +1,54 @@ +#include +#include "icmp_extension.h" +#include "endianness.h" +#include "exceptions.h" + +using std::runtime_error; + +namespace Tins { + +const uint32_t ICMPExtension::BASE_HEADER_SIZE = sizeof(uint16_t) + sizeof(uint8_t) * 2; + +ICMPExtension::ICMPExtension(const uint8_t* buffer, uint32_t total_sz) { + // Check for the base header (u16 length + u8 clss + u8 type) + if (total_sz < BASE_HEADER_SIZE) { + throw malformed_packet(); + } + + uint16_t length = Endian::be_to_host(*(const uint16_t*)buffer); + buffer += sizeof(uint16_t); + extension_class_ = *buffer++; + extension_type_ = *buffer++; + total_sz -= BASE_HEADER_SIZE; + // Length is BASE_HEADER_SIZE + payload size, make sure it's valid + if (length < BASE_HEADER_SIZE || length - BASE_HEADER_SIZE > total_sz) { + throw malformed_packet(); + } + length -= BASE_HEADER_SIZE; + payload_.assign(buffer, buffer + length); +} + +uint32_t ICMPExtension::extension_size() const { + return BASE_HEADER_SIZE + payload_.size(); +} + +void ICMPExtension::serialize(uint8_t* buffer, uint32_t buffer_size) const { + if (buffer_size < extension_size()) { + throw runtime_error("Serialization buffer is too small"); + } + *(uint16_t*)buffer = Endian::host_to_be(extension_size()); + buffer += sizeof(uint16_t); + *buffer = extension_class_; + buffer += sizeof(uint8_t); + *buffer = extension_type_; + buffer += sizeof(uint8_t); + copy(payload_.begin(), payload_.end(), buffer); +} + +ICMPExtension::serialization_type ICMPExtension::serialize() const { + serialization_type output(extension_size()); + serialize(&output[0], output.size()); + return output; +} + +} // Tins diff --git a/tests/src/CMakeLists.txt b/tests/src/CMakeLists.txt index 7d282c4..fe5befc 100644 --- a/tests/src/CMakeLists.txt +++ b/tests/src/CMakeLists.txt @@ -58,6 +58,7 @@ ADD_CUSTOM_TARGET( Dot1QTest EthernetTest HWAddressTest + ICMPExtensionTest ICMPTest ICMPv6Test IPTest @@ -100,6 +101,7 @@ ADD_EXECUTABLE(DNSTest EXCLUDE_FROM_ALL dns.cpp) ADD_EXECUTABLE(Dot1QTest EXCLUDE_FROM_ALL dot1q.cpp) ADD_EXECUTABLE(EthernetTest EXCLUDE_FROM_ALL ethernetII.cpp) ADD_EXECUTABLE(HWAddressTest EXCLUDE_FROM_ALL hwaddress.cpp) +ADD_EXECUTABLE(ICMPExtensionTest EXCLUDE_FROM_ALL icmp_extension.cpp) ADD_EXECUTABLE(ICMPTest EXCLUDE_FROM_ALL icmp.cpp) ADD_EXECUTABLE(ICMPv6Test EXCLUDE_FROM_ALL icmpv6.cpp) ADD_EXECUTABLE(IPTest EXCLUDE_FROM_ALL ip.cpp) @@ -179,6 +181,7 @@ ADD_TEST(Dot11RTS Dot11RTSTest) ADD_TEST(Dot1Q Dot1QTest) ADD_TEST(Ethernet EthernetTest) ADD_TEST(HWAddress HWAddressTest) +ADD_TEST(ICMPExtension ICMPExtensionTest) ADD_TEST(ICMP ICMPTest) ADD_TEST(ICMPv6 ICMPv6Test) ADD_TEST(IP IPTest) diff --git a/tests/src/icmp_extension.cpp b/tests/src/icmp_extension.cpp new file mode 100644 index 0000000..da118bf --- /dev/null +++ b/tests/src/icmp_extension.cpp @@ -0,0 +1,27 @@ +#include +#include "icmp_extension.h" + +using Tins::ICMPExtension; + +class ICMPExtensionTest : public testing::Test { +public: + +}; + +TEST_F(ICMPExtensionTest, ConstructorFromBuffer) { + const uint8_t input[] = { 0, 8, 1, 1, 24, 150, 1, 1 }; + const uint8_t payload[] = { 24, 150, 1, 1 }; + ICMPExtension ext(input, sizeof(input)); + EXPECT_EQ(1, ext.extension_class()); + EXPECT_EQ(1, ext.extension_type()); + EXPECT_EQ( + ICMPExtension::payload_type(payload, payload + sizeof(payload)), + ext.payload() + ); + + ICMPExtension::serialization_type buffer = ext.serialize(); + EXPECT_EQ( + ICMPExtension::serialization_type(input, input + sizeof(input)), + buffer + ); +}