1
0
mirror of https://github.com/mfontanini/libtins synced 2026-01-23 02:35:57 +01:00

Add extensions for ICMPv6

This commit is contained in:
Matias Fontanini
2015-12-21 12:33:52 -08:00
parent 530cc56922
commit 6d90b0ce32
11 changed files with 779 additions and 474 deletions

View File

@@ -50,15 +50,15 @@
namespace Tins {
/**
/**
* \class ICMP
* \brief Class that represents an 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:
class ICMP : public PDU {
public:
/**
* \brief This PDU's flag.
*/
@@ -303,14 +303,14 @@ namespace Tins {
*
* \return Returns the pointer field value.
*/
uint8_t pointer() const { return this->_icmp.un.rfc4884.pointer; }
uint8_t pointer() const { return _icmp.un.rfc4884.pointer; }
/**
* \brief Getter for the length field.
*
* \return Returns the length field value.
*/
uint8_t length() const { return this->_icmp.un.rfc4884.length; }
uint8_t length() const { return _icmp.un.rfc4884.length; }
/**
* \brief Getter for the mtu field.
@@ -426,9 +426,7 @@ namespace Tins {
ICMP *clone() const {
return new ICMP(*this);
}
private:
static const uint32_t EXTENSION_PAYLOAD_LIMIT;
private:
TINS_BEGIN_PACK
struct icmphdr {
uint8_t type;
@@ -461,14 +459,15 @@ namespace Tins {
*/
void write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *parent);
uint32_t get_adjusted_inner_pdu_size() const;
void try_parse_extensions(const uint8_t* buffer, uint32_t& total_sz);
bool are_extensions_allowed() const;
uint32_t get_adjusted_inner_pdu_size() const;
icmphdr _icmp;
uint32_t _orig_timestamp_or_address_mask, _recv_timestamp, _trans_timestamp;
ICMPExtensionsStructure extensions_;
};
}
};
} // Tins
#endif // TINS_ICMP_H

View File

@@ -123,6 +123,12 @@ private:
*/
class ICMPExtensionsStructure {
public:
/**
* The minimum ICMP payload size that has to be present when the PDU
* contains extensions.
*/
static const uint32_t MINIMUM_ICMP_PAYLOAD;
/**
* The type that will be returned when serializing an extensions
* structure object

View File

@@ -40,6 +40,7 @@
#include "small_uint.h"
#include "hw_address.h"
#include "small_uint.h"
#include "icmp_extension.h"
#include "cxxstd.h"
namespace Tins {
@@ -692,6 +693,15 @@ public:
return _options;
}
/**
* \brief Getter for the length field.
*
* \return Returns the length field value.
*/
uint8_t length() const {
return _header.rfc4884.length;
}
// Setters
/**
@@ -872,6 +882,51 @@ public:
*/
uint32_t header_size() const;
/**
* \brief Returns the trailer size.
*
* This metod overrides PDU::trailer_size. This size will hold the extensions size
*
* \sa PDU::header_size
*/
uint32_t trailer_size() const;
/**
* \brief Getter for the extensions field.
*
* \return The extensions field
*/
const ICMPExtensionsStructure& extensions() const { return extensions_; }
/**
* \brief Getter for the extensions field.
*
* \return The extensions field
*/
ICMPExtensionsStructure& extensions() { return extensions_; }
/**
* \brief Indicates whether this object contains ICMP extensions
*/
bool has_extensions() const { return !extensions_.extensions().empty(); }
/**
* \brief Sets whether the length field will be set for packets that use it
*
* As defined in RFC 4884, some ICMP packet types can have a length field. This
* method controlers whether the length field is set or not.
*
* Note that this only indicates that the packet should use this field. The
* actual value will be set during the packet's serialization.
*
* Note that, in order to br RFC compliant, if the size of the encapsulated
* PDU is greater than 128, the length field will always be set, regardless
* of whether this method was called or not.
*
* /param value true iff the length field should be set appropriately
*/
void use_length_field(bool value);
/**
* \brief Check wether ptr points to a valid response for this PDU.
*
@@ -1310,6 +1365,10 @@ private:
#endif
uint16_t router_lifetime;
} u_nd_ra;
struct {
uint8_t length;
uint8_t unused[3];
} rfc4884;
};
} TINS_END_PACK;
@@ -1322,6 +1381,9 @@ private:
addr_list_type search_addr_list(OptionTypes type) const;
options_type::const_iterator search_option_iterator(OptionTypes type) const;
options_type::iterator search_option_iterator(OptionTypes type);
void try_parse_extensions(const uint8_t* buffer, uint32_t& total_sz);
bool are_extensions_allowed() const;
uint32_t get_adjusted_inner_pdu_size() const;
template<template <typename> class Functor>
const option *safe_search_option(OptionTypes opt, uint32_t size) const {
@@ -1344,6 +1406,7 @@ private:
options_type _options;
uint32_t _options_size;
uint32_t reach_time, retrans_timer;
ICMPExtensionsStructure extensions_;
};
}

View File

@@ -47,6 +47,7 @@
namespace Tins {
class IPv4Address;
class IPv6Address;
class ICMPExtensionsStructure;
namespace Internals {
template<size_t n>
@@ -124,6 +125,10 @@ PDU *pdu_from_flag(PDU::PDUType type, const uint8_t *buffer, uint32_t size);
Constants::Ethernet::e pdu_flag_to_ether_type(PDU::PDUType flag);
Constants::IP::e pdu_flag_to_ip_type(PDU::PDUType flag);
uint32_t get_padded_icmp_inner_pdu_size(const PDU* inner_pdu, uint32_t pad_alignment);
void try_parse_icmp_extensions(const uint8_t* buffer, uint32_t& total_sz,
uint32_t payload_length, ICMPExtensionsStructure& extensions);
template<typename T>
bool increment_buffer(T &addr) {
typename T::iterator it = addr.end() - 1;

View File

@@ -42,8 +42,6 @@
namespace Tins {
const uint32_t ICMP::EXTENSION_PAYLOAD_LIMIT = 128;
ICMP::ICMP(Flags flag)
: _orig_timestamp_or_address_mask(), _recv_timestamp(), _trans_timestamp()
{
@@ -258,14 +256,14 @@ void ICMP::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *)
uint32_t inner_pdu_size = get_adjusted_inner_pdu_size();
// If it's lower than 128, we need to padd enough zeroes to make it 128 bytes long
if (inner_pdu_size < 128) {
memset(buffer + sizeof(icmphdr) + inner_pdu_size, 0, 128 - inner_pdu_size);
memset(extensions_ptr + inner_pdu_size, 0, 128 - inner_pdu_size);
inner_pdu_size = 128;
}
else {
// If the packet has to be padded to 32 bits, append the amount
// of zeroes we need
uint32_t diff = inner_pdu_size - inner_pdu()->size();
memset(buffer + sizeof(icmphdr) + inner_pdu_size, 0, diff);
memset(extensions_ptr + inner_pdu_size, 0, diff);
}
extensions_ptr += inner_pdu_size;
}
@@ -287,46 +285,14 @@ void ICMP::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *)
uint32_t ICMP::get_adjusted_inner_pdu_size() const {
// This gets the size of the next pdu, padded to the next 32 bit word boundary
if (inner_pdu()) {
uint32_t inner_pdu_size = inner_pdu()->size();
uint32_t padding = inner_pdu_size % 4;
inner_pdu_size = padding ? (inner_pdu_size - padding + 4) : inner_pdu_size;
return inner_pdu_size;
}
else {
return 0;
}
return Internals::get_padded_icmp_inner_pdu_size(inner_pdu(), sizeof(uint32_t));
}
void ICMP::try_parse_extensions(const uint8_t* buffer, uint32_t& total_sz) {
if (total_sz == 0) {
return;
}
// Check if this is one of the types defined in RFC 4884
if (are_extensions_allowed()) {
uint32_t actual_length = length() * sizeof(uint32_t);
// Check if we actually have this amount of data and whether it's more than
// the minimum encapsulated packet size
const uint8_t* extensions_ptr;
uint32_t extensions_size;
if (actual_length < total_sz && actual_length >= EXTENSION_PAYLOAD_LIMIT) {
extensions_ptr = buffer + actual_length;
extensions_size = total_sz - actual_length;
}
else if (total_sz > EXTENSION_PAYLOAD_LIMIT) {
// This packet might be non-rfc compliant. In that case the length
// field can contain garbage.
extensions_ptr = buffer + EXTENSION_PAYLOAD_LIMIT;
extensions_size = total_sz - EXTENSION_PAYLOAD_LIMIT;
}
else {
// No more special cases, this doesn't have extensions
return;
}
if (ICMPExtensionsStructure::validate_extensions(extensions_ptr, extensions_size)) {
extensions_ = ICMPExtensionsStructure(extensions_ptr, extensions_size);
total_sz -= extensions_size;
}
Internals::try_parse_icmp_extensions(buffer, total_sz, length() * sizeof(uint32_t),
extensions_);
}
}

View File

@@ -79,6 +79,7 @@ ICMPExtension::serialization_type ICMPExtension::serialize() const {
// ICMPExtensionsStructure class
const uint32_t ICMPExtensionsStructure::MINIMUM_ICMP_PAYLOAD = 128;
const uint32_t ICMPExtensionsStructure::BASE_HEADER_SIZE = sizeof(uint16_t) * 2;
ICMPExtensionsStructure::ICMPExtensionsStructure()
@@ -148,7 +149,6 @@ void ICMPExtensionsStructure::add_extension(const ICMPExtension& extension) {
void ICMPExtensionsStructure::serialize(uint8_t* buffer, uint32_t buffer_size) {
const uint32_t structure_size = size();
if (buffer_size < structure_size) {
std::cout << buffer_size << " vs " << structure_size << std::endl;
throw malformed_packet();
}
uint8_t* original_ptr = buffer;

View File

@@ -50,38 +50,47 @@ ICMPv6::ICMPv6(Types tp)
ICMPv6::ICMPv6(const uint8_t *buffer, uint32_t total_sz)
: _options_size(), reach_time(0), retrans_timer(0)
{
if(total_sz < sizeof(_header))
if (total_sz < sizeof(_header)) {
throw malformed_packet();
}
std::memcpy(&_header, buffer, sizeof(_header));
buffer += sizeof(_header);
total_sz -= sizeof(_header);
if(has_target_addr()) {
if(total_sz < ipaddress_type::address_size)
if (has_target_addr()) {
if(total_sz < ipaddress_type::address_size) {
throw malformed_packet();
}
target_addr(buffer);
buffer += ipaddress_type::address_size;
total_sz -= ipaddress_type::address_size;
}
if(has_dest_addr()) {
if(total_sz < ipaddress_type::address_size)
if (has_dest_addr()) {
if(total_sz < ipaddress_type::address_size) {
throw malformed_packet();
}
dest_addr(buffer);
buffer += ipaddress_type::address_size;
total_sz -= ipaddress_type::address_size;
}
if(type() == ROUTER_ADVERT) {
if(total_sz < sizeof(uint32_t) * 2)
if (type() == ROUTER_ADVERT) {
if(total_sz < sizeof(uint32_t) * 2) {
throw malformed_packet();
}
memcpy(&reach_time, buffer, sizeof(uint32_t));
memcpy(&retrans_timer, buffer + sizeof(uint32_t), sizeof(uint32_t));
buffer += sizeof(uint32_t) * 2;
total_sz -= sizeof(uint32_t) * 2;
}
if(has_options())
// Retrieve options
if (has_options()) {
parse_options(buffer, total_sz);
if(total_sz > 0)
}
// Attempt to parse ICMP extensions
try_parse_extensions(buffer, total_sz);
if (total_sz) {
inner_pdu(new RawPDU(buffer, total_sz));
}
}
void ICMPv6::parse_options(const uint8_t *&buffer, uint32_t &total_sz) {
@@ -182,6 +191,28 @@ uint32_t ICMPv6::header_size() const {
(has_dest_addr() ? ipaddress_type::address_size : 0);
}
uint32_t ICMPv6::trailer_size() const {
uint32_t output = 0;
if (has_extensions()) {
output += extensions_.size();
if (inner_pdu()) {
// This gets how much padding we'll use.
// If the next pdu size is lower than 128 bytes, then padding = 128 - pdu size
// If the next pdu size is greater than 128 bytes,
// then padding = pdu size padded to next 32 bit boundary - pdu size
const uint32_t upper_bound = std::max(get_adjusted_inner_pdu_size(), 128U);
output += upper_bound - inner_pdu()->size();
}
}
return output;
}
void ICMPv6::use_length_field(bool value) {
// We just need a non 0 value here, we'll use the right value on
// write_serialization
_header.rfc4884.length = value ? 1 : 0;
}
bool ICMPv6::matches_response(const uint8_t *ptr, uint32_t total_sz) const {
if(total_sz < sizeof(icmp6hdr))
return false;
@@ -199,6 +230,18 @@ void ICMPv6::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *
uint32_t full_sz = total_sz;
uint8_t *buffer_start = buffer;
_header.cksum = 0;
// If extensions are allowed and we have to set the length field
if (are_extensions_allowed()) {
uint32_t length_value = get_adjusted_inner_pdu_size();
// If the next pdu size is greater than 128, we are forced to set the length field
if (length() != 0 || length_value > 128) {
length_value = length_value ? std::max(length_value, 128U) : 0;
// This field uses 64 bit words as the unit
_header.rfc4884.length = length_value / sizeof(uint64_t);
}
}
std::memcpy(buffer, &_header, sizeof(_header));
buffer += sizeof(_header);
total_sz -= sizeof(_header);
@@ -225,6 +268,30 @@ void ICMPv6::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *
#endif
buffer = write_option(*it, buffer);
}
if (has_extensions()) {
uint8_t* extensions_ptr = buffer;
if (inner_pdu()) {
// Get the size of the next pdu, padded to the next 32 bit boundary
uint32_t inner_pdu_size = get_adjusted_inner_pdu_size();
// If it's lower than 128, we need to padd enough zeroes to make it 128 bytes long
if (inner_pdu_size < 128) {
memset(extensions_ptr + inner_pdu_size, 0,
128 - inner_pdu_size);
inner_pdu_size = 128;
}
else {
// If the packet has to be padded to 64 bits, append the amount
// of zeroes we need
uint32_t diff = inner_pdu_size - inner_pdu()->size();
memset(extensions_ptr + inner_pdu_size, 0, diff);
}
extensions_ptr += inner_pdu_size;
}
// Now serialize the exensions where they should be
extensions_.serialize(extensions_ptr, total_sz - (extensions_ptr - buffer));
}
const Tins::IPv6 *ipv6 = tins_cast<const Tins::IPv6*>(parent);
if(ipv6) {
uint32_t checksum = Utils::pseudoheader_checksum(
@@ -581,12 +648,30 @@ void ICMPv6::dns_search_list(const dns_search_list_type &value) {
buffer.push_back(0);
}
uint8_t padding = 8 - (buffer.size() + 2) % 8;
if(padding == 8)
if (padding == 8) {
padding = 0;
}
buffer.insert(buffer.end(), padding, 0);
add_option(option(DNS_SEARCH_LIST, buffer.begin(), buffer.end()));
}
uint32_t ICMPv6::get_adjusted_inner_pdu_size() const {
// This gets the size of the next pdu, padded to the next 64 bit word boundary
return Internals::get_padded_icmp_inner_pdu_size(inner_pdu(), sizeof(uint64_t));
}
void ICMPv6::try_parse_extensions(const uint8_t* buffer, uint32_t& total_sz) {
// Check if this is one of the types defined in RFC 4884
if (are_extensions_allowed()) {
Internals::try_parse_icmp_extensions(buffer, total_sz, length() * sizeof(uint64_t),
extensions_);
}
}
bool ICMPv6::are_extensions_allowed() const {
return type() == TIME_EXCEEDED;
}
// ********************************************************************
// Option getters
// ********************************************************************

View File

@@ -262,6 +262,50 @@ Constants::IP::e pdu_flag_to_ip_type(PDU::PDUType flag) {
};
}
uint32_t get_padded_icmp_inner_pdu_size(const PDU* inner_pdu, uint32_t pad_alignment) {
// This gets the size of the next pdu, padded to the next 32 bit word boundary
if (inner_pdu) {
uint32_t inner_pdu_size = inner_pdu->size();
uint32_t padding = inner_pdu_size % pad_alignment;
inner_pdu_size = padding ? (inner_pdu_size - padding + pad_alignment) : inner_pdu_size;
return inner_pdu_size;
}
else {
return 0;
}
}
void try_parse_icmp_extensions(const uint8_t* buffer, uint32_t& total_sz,
uint32_t payload_length, ICMPExtensionsStructure& extensions) {
if (total_sz == 0) {
return;
}
// Check if this is one of the types defined in RFC 4884
const uint32_t minimum_payload = ICMPExtensionsStructure::MINIMUM_ICMP_PAYLOAD;
// Check if we actually have this amount of data and whether it's more than
// the minimum encapsulated packet size
const uint8_t* extensions_ptr;
uint32_t extensions_size;
if (payload_length < total_sz && payload_length >= minimum_payload) {
extensions_ptr = buffer + payload_length;
extensions_size = total_sz - payload_length;
}
else if (total_sz > minimum_payload) {
// This packet might be non-rfc compliant. In that case the length
// field can contain garbage.
extensions_ptr = buffer + minimum_payload;
extensions_size = total_sz - minimum_payload;
}
else {
// No more special cases, this doesn't have extensions
return;
}
if (ICMPExtensionsStructure::validate_extensions(extensions_ptr, extensions_size)) {
extensions = ICMPExtensionsStructure(extensions_ptr, extensions_size);
total_sz -= extensions_size;
}
}
bool increment(IPv4Address &addr) {
uint32_t addr_int = Endian::be_to_host<uint32_t>(addr);
bool reached_end = ++addr_int == 0xffffffff;

View File

@@ -7,6 +7,7 @@
#include "ip.h"
#include "tcp.h"
#include "utils.h"
#include "rawpdu.h"
#include "hw_address.h"
using namespace Tins;
@@ -16,6 +17,8 @@ public:
static const uint8_t expected_packet[];
static const uint8_t expected_packet1[];
static const uint8_t expected_packet2[];
static const uint8_t packet_with_extensions[];
static const uint8_t packet_with_extensions_and_length[];
void test_equals(const ICMPv6 &icmp1, const ICMPv6 &icmp2);
};
@@ -42,6 +45,24 @@ const uint8_t ICMPv6Test::expected_packet2[] = {
128, 218
};
const uint8_t ICMPv6Test::packet_with_extensions[] = {
3, 0, 139, 66, 0, 0, 0, 0, 96, 0, 0, 0, 0, 38, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 255, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 12, 0, 99, 0, 38, 45, 93,
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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32,
0, 197, 95, 0, 8, 1, 1, 24, 150, 1, 1
};
const uint8_t ICMPv6Test::packet_with_extensions_and_length[] = {
3, 0, 139, 66, 16, 0, 0, 0, 96, 0, 0, 0, 0, 38, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 255, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 12, 0, 99, 0, 38, 45, 93,
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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32,
0, 197, 95, 0, 8, 1, 1, 24, 150, 1, 1
};
TEST_F(ICMPv6Test, Constructor) {
ICMPv6 icmp;
EXPECT_EQ(icmp.type(), ICMPv6::ECHO_REQUEST);
@@ -486,3 +507,40 @@ TEST_F(ICMPv6Test, RemoveOption) {
PDU::serialization_type new_buffer = icmp.serialize();
EXPECT_EQ(old_buffer, new_buffer);
}
TEST_F(ICMPv6Test, ExtensionsParsingWithoutALengthField) {
const uint8_t encapsulated[] = { 96, 0, 0, 0, 0, 38, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 12, 0, 99, 0, 38, 45, 93, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
const uint8_t ext[] = { 0, 8, 1, 1, 24, 150, 1, 1 };
ICMPv6 icmp(packet_with_extensions, sizeof(packet_with_extensions));
ICMPExtensionsStructure extensions = icmp.extensions();
ASSERT_EQ(1, extensions.extensions().size());
EXPECT_EQ(
ICMPExtension::payload_type(ext, ext + sizeof(ext)),
extensions.extensions().begin()->serialize()
);
const RawPDU* raw = icmp.find_pdu<RawPDU>();
ASSERT_TRUE(raw != 0);
EXPECT_EQ(sizeof(encapsulated), raw->payload().size());
EXPECT_EQ(
RawPDU::payload_type(encapsulated, encapsulated + sizeof(encapsulated)),
raw->payload()
);
}
TEST_F(ICMPv6Test, ExtensionsParsingWithALengthField) {
const uint8_t encapsulated[] = { 96, 0, 0, 0, 0, 38, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 12, 0, 99, 0, 38, 45, 93, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
const uint8_t ext[] = { 0, 8, 1, 1, 24, 150, 1, 1 };
ICMPv6 icmp(packet_with_extensions_and_length, sizeof(packet_with_extensions_and_length));
ICMPExtensionsStructure extensions = icmp.extensions();
ASSERT_EQ(1, extensions.extensions().size());
EXPECT_EQ(
ICMPExtension::payload_type(ext, ext + sizeof(ext)),
extensions.extensions().begin()->serialize()
);
const RawPDU* raw = icmp.find_pdu<RawPDU>();
ASSERT_TRUE(raw != 0);
EXPECT_EQ(
RawPDU::payload_type(encapsulated, encapsulated + sizeof(encapsulated)),
raw->payload()
);
}

View File

@@ -785,6 +785,17 @@ TEST_F(IPTest, FragmentOffset) {
EXPECT_FALSE(ip.is_fragmented());
}
// Make sure that a big payload is not considered ICMP extensions
TEST_F(IPTest, BigEncapsulatedPacketIsNotConsideredToHaveExtensions) {
IP encapsulated = IP(TINS_DEFAULT_TEST_IP) / UDP(99, 12) / RawPDU(std::string(250, 'A'));
EthernetII pkt = EthernetII() / IP() / ICMP(ICMP::TIME_EXCEEDED) / encapsulated;
PDU::serialization_type buffer = pkt.serialize();
EthernetII serialized(&buffer[0], buffer.size());
ASSERT_EQ(encapsulated.size(), serialized.rfind_pdu<RawPDU>().payload().size());
ASSERT_TRUE(serialized.rfind_pdu<ICMP>().extensions().extensions().empty());
}
// Use a large buffer. This wil set the length field
TEST_F(IPTest, SerializePacketHavingICMPExtensionsWithLengthAndLotsOfPayload) {
IP encapsulated = IP(TINS_DEFAULT_TEST_IP) / UDP(99, 12) / RawPDU(std::string(250, 'A'));

View File

@@ -7,12 +7,20 @@
#include "udp.h"
#include "icmp.h"
#include "icmpv6.h"
#include "rawpdu.h"
#include "ethernetII.h"
#include "ipv6_address.h"
#include "utils.h"
using namespace std;
using namespace Tins;
#ifdef _WIN32
#define TINS_DEFAULT_TEST_IP "::"
#else
#define TINS_DEFAULT_TEST_IP "::1"
#endif
class IPv6Test : public testing::Test {
public:
static const uint8_t expected_packet1[], expected_packet2[];
@@ -175,3 +183,63 @@ TEST_F(IPv6Test, DestinationAddress) {
EXPECT_EQ(ipv6.dst_addr(), "99af:1293::1");
}
// Make sure that a big payload is not considered ICMP extensions
TEST_F(IPv6Test, BigEncapsulatedPacketIsNotConsideredToHaveExtensions) {
IPv6 encapsulated = IPv6(TINS_DEFAULT_TEST_IP) / UDP(99, 12) / RawPDU(std::string(250, 'A'));
EthernetII pkt = EthernetII() / IPv6() / ICMPv6(ICMPv6::TIME_EXCEEDED) / encapsulated;
PDU::serialization_type buffer = pkt.serialize();
EthernetII serialized(&buffer[0], buffer.size());
ASSERT_EQ(encapsulated.size(), serialized.rfind_pdu<RawPDU>().payload().size());
ASSERT_TRUE(serialized.rfind_pdu<ICMPv6>().extensions().extensions().empty());
}
// Use a large buffer. This wil set the length field
TEST_F(IPv6Test, SerializePacketHavingICMPExtensionsWithLengthAndLotsOfPayload) {
IPv6 encapsulated = IPv6(TINS_DEFAULT_TEST_IP) / UDP(99, 12) / RawPDU(std::string(250, 'A'));
EthernetII pkt = EthernetII() / IPv6() / ICMPv6(ICMPv6::TIME_EXCEEDED) / encapsulated;
const uint8_t payload[] = { 24, 150, 1, 1 };
ICMPExtension extension(1, 1);
ICMPExtension::payload_type ext_payload(payload, payload + sizeof(payload));
extension.payload(ext_payload);
pkt.rfind_pdu<ICMPv6>().extensions().add_extension(extension);
PDU::serialization_type buffer = pkt.serialize();
EthernetII serialized(&buffer[0], buffer.size());
ASSERT_EQ(1, serialized.rfind_pdu<ICMPv6>().extensions().extensions().size());
EXPECT_EQ(ext_payload, serialized.rfind_pdu<ICMPv6>().extensions().extensions().begin()->payload());
}
// Use a short buffer and set the length field
TEST_F(IPv6Test, SerializePacketHavingICMPExtensionsWithLengthAndShortPayload) {
IPv6 encapsulated = IPv6(TINS_DEFAULT_TEST_IP) / UDP(99, 12) / RawPDU(std::string(40, 'A'));
EthernetII pkt = EthernetII() / IPv6() / ICMPv6(ICMPv6::TIME_EXCEEDED) / encapsulated;
const uint8_t payload[] = { 24, 150, 1, 1 };
ICMPExtension extension(1, 1);
ICMPExtension::payload_type ext_payload(payload, payload + sizeof(payload));
extension.payload(ext_payload);
pkt.rfind_pdu<ICMPv6>().extensions().add_extension(extension);
pkt.rfind_pdu<ICMPv6>().use_length_field(true);
PDU::serialization_type buffer = pkt.serialize();
EthernetII serialized(&buffer[0], buffer.size());
ASSERT_EQ(1, serialized.rfind_pdu<ICMPv6>().extensions().extensions().size());
EXPECT_EQ(ext_payload, serialized.rfind_pdu<ICMPv6>().extensions().extensions().begin()->payload());
}
// Use a short buffer and don't set the length field
TEST_F(IPv6Test, SerializePacketHavingICMPExtensionsWithoutLengthAndShortPayload) {
IPv6 encapsulated = IPv6(TINS_DEFAULT_TEST_IP) / UDP(99, 12) / RawPDU(std::string(40, 'A'));
EthernetII pkt = EthernetII() / IPv6() / ICMPv6(ICMPv6::TIME_EXCEEDED) / encapsulated;
const uint8_t payload[] = { 24, 150, 1, 1 };
ICMPExtension extension(1, 1);
ICMPExtension::payload_type ext_payload(payload, payload + sizeof(payload));
extension.payload(ext_payload);
pkt.rfind_pdu<ICMPv6>().extensions().add_extension(extension);
pkt.rfind_pdu<ICMPv6>().use_length_field(false);
PDU::serialization_type buffer = pkt.serialize();
EthernetII serialized(&buffer[0], buffer.size());
ASSERT_EQ(1, serialized.rfind_pdu<ICMPv6>().extensions().extensions().size());
EXPECT_EQ(ext_payload, serialized.rfind_pdu<ICMPv6>().extensions().extensions().begin()->payload());
}