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,425 +50,424 @@
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
* \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:
/**
* \brief This PDU's flag.
*/
class ICMP : public PDU {
public:
/**
* \brief This PDU's flag.
*/
static const PDU::PDUType pdu_flag = PDU::ICMP;
/**
* The type used to store addresses.
*/
typedef IPv4Address address_type;
static const PDU::PDUType pdu_flag = PDU::ICMP;
/** \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,
TIMESTAMP_REQUEST = 13,
TIMESTAMP_REPLY = 14,
INFO_REQUEST = 15,
INFO_REPLY = 16,
ADDRESS_MASK_REQUEST = 17,
ADDRESS_MASK_REPLY = 18
};
/**
* The type used to store addresses.
*/
typedef IPv4Address address_type;
/**
* \brief Creates an instance of ICMP.
*
* If no flag is specified, then ECHO_REQUEST will be used.
* \param flag The type flag which will be set.
*/
ICMP(Flags flag = ECHO_REQUEST);
/**
* \brief Constructs an ICMP object from a buffer.
*
* If there is not enough size for an ICMP header, a
* malformed_packet exception is thrown.
*
* Any extra data in the buffer will be stored in a RawPDU.
*
* \param buffer The buffer from which this PDU will be constructed.
* \param total_sz The total size of the buffer.
*/
ICMP(const uint8_t *buffer, uint32_t total_sz);
/**
* \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 type The type which will be stored in the ICMP struct.
*/
void type(Flags type);
/**
* \brief Setter for the id field.
*
* \param new_id uint16_t with the new id.
*/
void id(uint16_t new_id);
/**
* \brief Setter for the sequence field.
*
* \param new_seq uint16_t with the new sequence.
*/
void sequence(uint16_t new_seq);
/**
* \brief Setter for the gateway field.
*
* \param new_gw The new value for the gateway field.
*/
void gateway(address_type new_gw);
/**
* \brief Setter for the mtu field.
*
* \param new_mtu uint16_t with the new sequence.
*/
void mtu(uint16_t new_mtu);
/**
* \brief Setter for the pointer field.
*
* \param new_pointer uint8_t with the new pointer.
*/
void pointer(uint8_t new_pointer);
/**
* \brief Setter for the original timestamp field.
*
* \param new_timestamp the value to be set.
*/
void original_timestamp(uint32_t new_timestamp);
/**
* \brief Setter for the receive timestamp field.
*
* \param new_timestamp the value to be set.
*/
void receive_timestamp(uint32_t new_timestamp);
/**
* \brief Setter for the transmit timestamp field.
*
* \param new_timestamp the value to be set.
*/
void transmit_timestamp(uint32_t new_timestamp);
/**
* \brief Setter for the address mask field.
*
* \param new_mask the value to be set.
*/
void address_mask(address_type new_mask);
/**
* \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 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 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, address_type address);
/**
* \brief Getter for the ICMP type flag.
*
* \return The type flag for this ICMP PDU.
*/
Flags type() const { return (Flags)_icmp.type; }
/**
* \brief Getter for the ICMP code flag.
*
* \return The code flag for this ICMP PDU.
*/
uint8_t code() const { return _icmp.code; }
/**
* \brief Getter for the checksum field.
*
* \return Returns the checksum as an unit16_t.
*/
uint16_t checksum() const { return Endian::be_to_host(_icmp.check); }
/**
* \brief Getter for the echo id.
*
* \return Returns the echo id.
*/
uint16_t id() const { return Endian::be_to_host(_icmp.un.echo.id); }
/**
* \brief Getter for the echo sequence number.
*
* \return Returns the echo sequence number.
*/
uint16_t sequence() const { return Endian::be_to_host(_icmp.un.echo.sequence); }
/**
* \brief Getter for the gateway field.
*
* \return Returns the gateway field value.
*/
address_type gateway() const {
return address_type(Endian::be_to_host(_icmp.un.gateway));
}
/**
* \brief Getter for the pointer field.
*
* \return Returns the pointer field value.
*/
uint8_t pointer() const { return this->_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; }
/**
* \brief Getter for the mtu field.
*
* \return Returns the mtu field value.
*/
uint16_t mtu() const { return Endian::be_to_host(_icmp.un.frag.mtu); }
/**
* \brief Getter for the original timestamp field.
*
* \return Returns the original timestamp value.
*/
uint32_t original_timestamp() const { return Endian::be_to_host(_orig_timestamp_or_address_mask); }
/**
* \brief Getter for the receive timestamp field.
*
* \return Returns the receive timestamp value.
*/
uint32_t receive_timestamp() const { return Endian::be_to_host(_recv_timestamp); }
/**
* \brief Getter for the transmit timestamp field.
*
* \return Returns the transmit timestamp value.
*/
uint32_t transmit_timestamp() const { return Endian::be_to_host(_trans_timestamp); }
/**
* \brief Getter for the address mask field.
*
* \return Returns the address mask value.
*/
address_type address_mask() const {
return address_type(Endian::be_to_host(_orig_timestamp_or_address_mask));
}
/**
* \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;
/**
* \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 Check wether ptr points to a valid response for this PDU.
*
* \sa PDU::matches_response
* \param ptr The pointer to the buffer.
* \param total_sz The size of the buffer.
*/
bool matches_response(const uint8_t *ptr, uint32_t total_sz) 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 Getter for the PDU's type.
*
* \sa PDU::pdu_type
*/
PDUType pdu_type() const { return PDU::ICMP; }
/**
* \sa PDU::clone
*/
ICMP *clone() const {
return new ICMP(*this);
}
private:
static const uint32_t EXTENSION_PAYLOAD_LIMIT;
TINS_BEGIN_PACK
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;
struct {
uint8_t pointer;
uint8_t length;
uint16_t unused;
} rfc4884;
} un;
} TINS_END_PACK;
void checksum(uint16_t new_check);
/** \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);
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_;
/** \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,
TIMESTAMP_REQUEST = 13,
TIMESTAMP_REPLY = 14,
INFO_REQUEST = 15,
INFO_REPLY = 16,
ADDRESS_MASK_REQUEST = 17,
ADDRESS_MASK_REPLY = 18
};
}
/**
* \brief Creates an instance of ICMP.
*
* If no flag is specified, then ECHO_REQUEST will be used.
* \param flag The type flag which will be set.
*/
ICMP(Flags flag = ECHO_REQUEST);
/**
* \brief Constructs an ICMP object from a buffer.
*
* If there is not enough size for an ICMP header, a
* malformed_packet exception is thrown.
*
* Any extra data in the buffer will be stored in a RawPDU.
*
* \param buffer The buffer from which this PDU will be constructed.
* \param total_sz The total size of the buffer.
*/
ICMP(const uint8_t *buffer, uint32_t total_sz);
/**
* \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 type The type which will be stored in the ICMP struct.
*/
void type(Flags type);
/**
* \brief Setter for the id field.
*
* \param new_id uint16_t with the new id.
*/
void id(uint16_t new_id);
/**
* \brief Setter for the sequence field.
*
* \param new_seq uint16_t with the new sequence.
*/
void sequence(uint16_t new_seq);
/**
* \brief Setter for the gateway field.
*
* \param new_gw The new value for the gateway field.
*/
void gateway(address_type new_gw);
/**
* \brief Setter for the mtu field.
*
* \param new_mtu uint16_t with the new sequence.
*/
void mtu(uint16_t new_mtu);
/**
* \brief Setter for the pointer field.
*
* \param new_pointer uint8_t with the new pointer.
*/
void pointer(uint8_t new_pointer);
/**
* \brief Setter for the original timestamp field.
*
* \param new_timestamp the value to be set.
*/
void original_timestamp(uint32_t new_timestamp);
/**
* \brief Setter for the receive timestamp field.
*
* \param new_timestamp the value to be set.
*/
void receive_timestamp(uint32_t new_timestamp);
/**
* \brief Setter for the transmit timestamp field.
*
* \param new_timestamp the value to be set.
*/
void transmit_timestamp(uint32_t new_timestamp);
/**
* \brief Setter for the address mask field.
*
* \param new_mask the value to be set.
*/
void address_mask(address_type new_mask);
/**
* \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 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 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, address_type address);
/**
* \brief Getter for the ICMP type flag.
*
* \return The type flag for this ICMP PDU.
*/
Flags type() const { return (Flags)_icmp.type; }
/**
* \brief Getter for the ICMP code flag.
*
* \return The code flag for this ICMP PDU.
*/
uint8_t code() const { return _icmp.code; }
/**
* \brief Getter for the checksum field.
*
* \return Returns the checksum as an unit16_t.
*/
uint16_t checksum() const { return Endian::be_to_host(_icmp.check); }
/**
* \brief Getter for the echo id.
*
* \return Returns the echo id.
*/
uint16_t id() const { return Endian::be_to_host(_icmp.un.echo.id); }
/**
* \brief Getter for the echo sequence number.
*
* \return Returns the echo sequence number.
*/
uint16_t sequence() const { return Endian::be_to_host(_icmp.un.echo.sequence); }
/**
* \brief Getter for the gateway field.
*
* \return Returns the gateway field value.
*/
address_type gateway() const {
return address_type(Endian::be_to_host(_icmp.un.gateway));
}
/**
* \brief Getter for the pointer field.
*
* \return Returns the pointer field value.
*/
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 _icmp.un.rfc4884.length; }
/**
* \brief Getter for the mtu field.
*
* \return Returns the mtu field value.
*/
uint16_t mtu() const { return Endian::be_to_host(_icmp.un.frag.mtu); }
/**
* \brief Getter for the original timestamp field.
*
* \return Returns the original timestamp value.
*/
uint32_t original_timestamp() const { return Endian::be_to_host(_orig_timestamp_or_address_mask); }
/**
* \brief Getter for the receive timestamp field.
*
* \return Returns the receive timestamp value.
*/
uint32_t receive_timestamp() const { return Endian::be_to_host(_recv_timestamp); }
/**
* \brief Getter for the transmit timestamp field.
*
* \return Returns the transmit timestamp value.
*/
uint32_t transmit_timestamp() const { return Endian::be_to_host(_trans_timestamp); }
/**
* \brief Getter for the address mask field.
*
* \return Returns the address mask value.
*/
address_type address_mask() const {
return address_type(Endian::be_to_host(_orig_timestamp_or_address_mask));
}
/**
* \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;
/**
* \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 Check wether ptr points to a valid response for this PDU.
*
* \sa PDU::matches_response
* \param ptr The pointer to the buffer.
* \param total_sz The size of the buffer.
*/
bool matches_response(const uint8_t *ptr, uint32_t total_sz) 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 Getter for the PDU's type.
*
* \sa PDU::pdu_type
*/
PDUType pdu_type() const { return PDU::ICMP; }
/**
* \sa PDU::clone
*/
ICMP *clone() const {
return new ICMP(*this);
}
private:
TINS_BEGIN_PACK
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;
struct {
uint8_t pointer;
uint8_t length;
uint16_t unused;
} rfc4884;
} un;
} TINS_END_PACK;
void checksum(uint16_t new_check);
/** \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);
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;
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
/**
@@ -871,7 +881,52 @@ public:
* payload and options size. \sa PDU::header_size
*/
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.
*
@@ -1282,15 +1337,15 @@ private:
struct {
#if TINS_IS_LITTLE_ENDIAN
uint32_t reserved:5,
override:1,
solicited:1,
router:1,
reserved2:24;
override:1,
solicited:1,
router:1,
reserved2:24;
#else
uint32_t router:1,
solicited:1,
override:1,
reserved:29;
solicited:1,
override:1,
reserved:29;
#endif
} u_nd_advt;
struct {
@@ -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,7 +1381,10 @@ 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 {
const option *option = search_option(opt);
@@ -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());
}