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:
@@ -50,425 +50,424 @@
|
|||||||
|
|
||||||
namespace Tins {
|
namespace Tins {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \class ICMP
|
* \class ICMP
|
||||||
* \brief Class that represents an ICMP PDU.
|
* \brief Class that represents an ICMP PDU.
|
||||||
*
|
*
|
||||||
* ICMP is the representation of the ICMP PDU. Instances of this class
|
* ICMP is the representation of the ICMP PDU. Instances of this class
|
||||||
* must be sent over a level 3 PDU, this will otherwise fail.
|
* 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 {
|
static const PDU::PDUType pdu_flag = PDU::ICMP;
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* \brief This PDU's flag.
|
|
||||||
*/
|
|
||||||
static const PDU::PDUType pdu_flag = PDU::ICMP;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type used to store addresses.
|
|
||||||
*/
|
|
||||||
typedef IPv4Address address_type;
|
|
||||||
|
|
||||||
/** \brief ICMP flags
|
/**
|
||||||
*/
|
* The type used to store addresses.
|
||||||
enum Flags {
|
*/
|
||||||
ECHO_REPLY = 0,
|
typedef IPv4Address address_type;
|
||||||
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 ICMP flags
|
||||||
* \brief Creates an instance of ICMP.
|
*/
|
||||||
*
|
enum Flags {
|
||||||
* If no flag is specified, then ECHO_REQUEST will be used.
|
ECHO_REPLY = 0,
|
||||||
* \param flag The type flag which will be set.
|
DEST_UNREACHABLE = 3,
|
||||||
*/
|
SOURCE_QUENCH = 4,
|
||||||
ICMP(Flags flag = ECHO_REQUEST);
|
REDIRECT = 5,
|
||||||
|
ECHO_REQUEST = 8,
|
||||||
/**
|
TIME_EXCEEDED = 11,
|
||||||
* \brief Constructs an ICMP object from a buffer.
|
PARAM_PROBLEM = 12,
|
||||||
*
|
TIMESTAMP_REQUEST = 13,
|
||||||
* If there is not enough size for an ICMP header, a
|
TIMESTAMP_REPLY = 14,
|
||||||
* malformed_packet exception is thrown.
|
INFO_REQUEST = 15,
|
||||||
*
|
INFO_REPLY = 16,
|
||||||
* Any extra data in the buffer will be stored in a RawPDU.
|
ADDRESS_MASK_REQUEST = 17,
|
||||||
*
|
ADDRESS_MASK_REPLY = 18
|
||||||
* \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 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
|
#endif // TINS_ICMP_H
|
||||||
|
|||||||
@@ -123,6 +123,12 @@ private:
|
|||||||
*/
|
*/
|
||||||
class ICMPExtensionsStructure {
|
class ICMPExtensionsStructure {
|
||||||
public:
|
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
|
* The type that will be returned when serializing an extensions
|
||||||
* structure object
|
* structure object
|
||||||
|
|||||||
@@ -40,6 +40,7 @@
|
|||||||
#include "small_uint.h"
|
#include "small_uint.h"
|
||||||
#include "hw_address.h"
|
#include "hw_address.h"
|
||||||
#include "small_uint.h"
|
#include "small_uint.h"
|
||||||
|
#include "icmp_extension.h"
|
||||||
#include "cxxstd.h"
|
#include "cxxstd.h"
|
||||||
|
|
||||||
namespace Tins {
|
namespace Tins {
|
||||||
@@ -692,6 +693,15 @@ public:
|
|||||||
return _options;
|
return _options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Getter for the length field.
|
||||||
|
*
|
||||||
|
* \return Returns the length field value.
|
||||||
|
*/
|
||||||
|
uint8_t length() const {
|
||||||
|
return _header.rfc4884.length;
|
||||||
|
}
|
||||||
|
|
||||||
// Setters
|
// Setters
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -871,7 +881,52 @@ public:
|
|||||||
* payload and options size. \sa PDU::header_size
|
* payload and options size. \sa PDU::header_size
|
||||||
*/
|
*/
|
||||||
uint32_t header_size() const;
|
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.
|
* \brief Check wether ptr points to a valid response for this PDU.
|
||||||
*
|
*
|
||||||
@@ -1282,15 +1337,15 @@ private:
|
|||||||
struct {
|
struct {
|
||||||
#if TINS_IS_LITTLE_ENDIAN
|
#if TINS_IS_LITTLE_ENDIAN
|
||||||
uint32_t reserved:5,
|
uint32_t reserved:5,
|
||||||
override:1,
|
override:1,
|
||||||
solicited:1,
|
solicited:1,
|
||||||
router:1,
|
router:1,
|
||||||
reserved2:24;
|
reserved2:24;
|
||||||
#else
|
#else
|
||||||
uint32_t router:1,
|
uint32_t router:1,
|
||||||
solicited:1,
|
solicited:1,
|
||||||
override:1,
|
override:1,
|
||||||
reserved:29;
|
reserved:29;
|
||||||
#endif
|
#endif
|
||||||
} u_nd_advt;
|
} u_nd_advt;
|
||||||
struct {
|
struct {
|
||||||
@@ -1310,6 +1365,10 @@ private:
|
|||||||
#endif
|
#endif
|
||||||
uint16_t router_lifetime;
|
uint16_t router_lifetime;
|
||||||
} u_nd_ra;
|
} u_nd_ra;
|
||||||
|
struct {
|
||||||
|
uint8_t length;
|
||||||
|
uint8_t unused[3];
|
||||||
|
} rfc4884;
|
||||||
};
|
};
|
||||||
} TINS_END_PACK;
|
} TINS_END_PACK;
|
||||||
|
|
||||||
@@ -1322,7 +1381,10 @@ private:
|
|||||||
addr_list_type search_addr_list(OptionTypes type) const;
|
addr_list_type search_addr_list(OptionTypes type) const;
|
||||||
options_type::const_iterator search_option_iterator(OptionTypes type) const;
|
options_type::const_iterator search_option_iterator(OptionTypes type) const;
|
||||||
options_type::iterator search_option_iterator(OptionTypes type);
|
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>
|
template<template <typename> class Functor>
|
||||||
const option *safe_search_option(OptionTypes opt, uint32_t size) const {
|
const option *safe_search_option(OptionTypes opt, uint32_t size) const {
|
||||||
const option *option = search_option(opt);
|
const option *option = search_option(opt);
|
||||||
@@ -1344,6 +1406,7 @@ private:
|
|||||||
options_type _options;
|
options_type _options;
|
||||||
uint32_t _options_size;
|
uint32_t _options_size;
|
||||||
uint32_t reach_time, retrans_timer;
|
uint32_t reach_time, retrans_timer;
|
||||||
|
ICMPExtensionsStructure extensions_;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,6 +47,7 @@
|
|||||||
namespace Tins {
|
namespace Tins {
|
||||||
class IPv4Address;
|
class IPv4Address;
|
||||||
class IPv6Address;
|
class IPv6Address;
|
||||||
|
class ICMPExtensionsStructure;
|
||||||
|
|
||||||
namespace Internals {
|
namespace Internals {
|
||||||
template<size_t n>
|
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::Ethernet::e pdu_flag_to_ether_type(PDU::PDUType flag);
|
||||||
Constants::IP::e pdu_flag_to_ip_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>
|
template<typename T>
|
||||||
bool increment_buffer(T &addr) {
|
bool increment_buffer(T &addr) {
|
||||||
typename T::iterator it = addr.end() - 1;
|
typename T::iterator it = addr.end() - 1;
|
||||||
|
|||||||
44
src/icmp.cpp
44
src/icmp.cpp
@@ -42,8 +42,6 @@
|
|||||||
|
|
||||||
namespace Tins {
|
namespace Tins {
|
||||||
|
|
||||||
const uint32_t ICMP::EXTENSION_PAYLOAD_LIMIT = 128;
|
|
||||||
|
|
||||||
ICMP::ICMP(Flags flag)
|
ICMP::ICMP(Flags flag)
|
||||||
: _orig_timestamp_or_address_mask(), _recv_timestamp(), _trans_timestamp()
|
: _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();
|
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 it's lower than 128, we need to padd enough zeroes to make it 128 bytes long
|
||||||
if (inner_pdu_size < 128) {
|
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;
|
inner_pdu_size = 128;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// If the packet has to be padded to 32 bits, append the amount
|
// If the packet has to be padded to 32 bits, append the amount
|
||||||
// of zeroes we need
|
// of zeroes we need
|
||||||
uint32_t diff = inner_pdu_size - inner_pdu()->size();
|
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;
|
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 {
|
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
|
// This gets the size of the next pdu, padded to the next 32 bit word boundary
|
||||||
if (inner_pdu()) {
|
return Internals::get_padded_icmp_inner_pdu_size(inner_pdu(), sizeof(uint32_t));
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ICMP::try_parse_extensions(const uint8_t* buffer, uint32_t& total_sz) {
|
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
|
// Check if this is one of the types defined in RFC 4884
|
||||||
if (are_extensions_allowed()) {
|
if (are_extensions_allowed()) {
|
||||||
uint32_t actual_length = length() * sizeof(uint32_t);
|
Internals::try_parse_icmp_extensions(buffer, total_sz, length() * sizeof(uint32_t),
|
||||||
// Check if we actually have this amount of data and whether it's more than
|
extensions_);
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ ICMPExtension::serialization_type ICMPExtension::serialize() const {
|
|||||||
|
|
||||||
// ICMPExtensionsStructure class
|
// ICMPExtensionsStructure class
|
||||||
|
|
||||||
|
const uint32_t ICMPExtensionsStructure::MINIMUM_ICMP_PAYLOAD = 128;
|
||||||
const uint32_t ICMPExtensionsStructure::BASE_HEADER_SIZE = sizeof(uint16_t) * 2;
|
const uint32_t ICMPExtensionsStructure::BASE_HEADER_SIZE = sizeof(uint16_t) * 2;
|
||||||
|
|
||||||
ICMPExtensionsStructure::ICMPExtensionsStructure()
|
ICMPExtensionsStructure::ICMPExtensionsStructure()
|
||||||
@@ -148,7 +149,6 @@ void ICMPExtensionsStructure::add_extension(const ICMPExtension& extension) {
|
|||||||
void ICMPExtensionsStructure::serialize(uint8_t* buffer, uint32_t buffer_size) {
|
void ICMPExtensionsStructure::serialize(uint8_t* buffer, uint32_t buffer_size) {
|
||||||
const uint32_t structure_size = size();
|
const uint32_t structure_size = size();
|
||||||
if (buffer_size < structure_size) {
|
if (buffer_size < structure_size) {
|
||||||
std::cout << buffer_size << " vs " << structure_size << std::endl;
|
|
||||||
throw malformed_packet();
|
throw malformed_packet();
|
||||||
}
|
}
|
||||||
uint8_t* original_ptr = buffer;
|
uint8_t* original_ptr = buffer;
|
||||||
|
|||||||
105
src/icmpv6.cpp
105
src/icmpv6.cpp
@@ -50,38 +50,47 @@ ICMPv6::ICMPv6(Types tp)
|
|||||||
ICMPv6::ICMPv6(const uint8_t *buffer, uint32_t total_sz)
|
ICMPv6::ICMPv6(const uint8_t *buffer, uint32_t total_sz)
|
||||||
: _options_size(), reach_time(0), retrans_timer(0)
|
: _options_size(), reach_time(0), retrans_timer(0)
|
||||||
{
|
{
|
||||||
if(total_sz < sizeof(_header))
|
if (total_sz < sizeof(_header)) {
|
||||||
throw malformed_packet();
|
throw malformed_packet();
|
||||||
|
}
|
||||||
std::memcpy(&_header, buffer, sizeof(_header));
|
std::memcpy(&_header, buffer, sizeof(_header));
|
||||||
buffer += sizeof(_header);
|
buffer += sizeof(_header);
|
||||||
total_sz -= sizeof(_header);
|
total_sz -= sizeof(_header);
|
||||||
if(has_target_addr()) {
|
if (has_target_addr()) {
|
||||||
if(total_sz < ipaddress_type::address_size)
|
if(total_sz < ipaddress_type::address_size) {
|
||||||
throw malformed_packet();
|
throw malformed_packet();
|
||||||
|
}
|
||||||
target_addr(buffer);
|
target_addr(buffer);
|
||||||
buffer += ipaddress_type::address_size;
|
buffer += ipaddress_type::address_size;
|
||||||
total_sz -= ipaddress_type::address_size;
|
total_sz -= ipaddress_type::address_size;
|
||||||
}
|
}
|
||||||
if(has_dest_addr()) {
|
if (has_dest_addr()) {
|
||||||
if(total_sz < ipaddress_type::address_size)
|
if(total_sz < ipaddress_type::address_size) {
|
||||||
throw malformed_packet();
|
throw malformed_packet();
|
||||||
|
}
|
||||||
dest_addr(buffer);
|
dest_addr(buffer);
|
||||||
buffer += ipaddress_type::address_size;
|
buffer += ipaddress_type::address_size;
|
||||||
total_sz -= ipaddress_type::address_size;
|
total_sz -= ipaddress_type::address_size;
|
||||||
}
|
}
|
||||||
if(type() == ROUTER_ADVERT) {
|
if (type() == ROUTER_ADVERT) {
|
||||||
if(total_sz < sizeof(uint32_t) * 2)
|
if(total_sz < sizeof(uint32_t) * 2) {
|
||||||
throw malformed_packet();
|
throw malformed_packet();
|
||||||
|
}
|
||||||
memcpy(&reach_time, buffer, sizeof(uint32_t));
|
memcpy(&reach_time, buffer, sizeof(uint32_t));
|
||||||
memcpy(&retrans_timer, buffer + sizeof(uint32_t), sizeof(uint32_t));
|
memcpy(&retrans_timer, buffer + sizeof(uint32_t), sizeof(uint32_t));
|
||||||
|
|
||||||
buffer += sizeof(uint32_t) * 2;
|
buffer += sizeof(uint32_t) * 2;
|
||||||
total_sz -= sizeof(uint32_t) * 2;
|
total_sz -= sizeof(uint32_t) * 2;
|
||||||
}
|
}
|
||||||
if(has_options())
|
// Retrieve options
|
||||||
|
if (has_options()) {
|
||||||
parse_options(buffer, total_sz);
|
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));
|
inner_pdu(new RawPDU(buffer, total_sz));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ICMPv6::parse_options(const uint8_t *&buffer, uint32_t &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);
|
(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 {
|
bool ICMPv6::matches_response(const uint8_t *ptr, uint32_t total_sz) const {
|
||||||
if(total_sz < sizeof(icmp6hdr))
|
if(total_sz < sizeof(icmp6hdr))
|
||||||
return false;
|
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;
|
uint32_t full_sz = total_sz;
|
||||||
uint8_t *buffer_start = buffer;
|
uint8_t *buffer_start = buffer;
|
||||||
_header.cksum = 0;
|
_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));
|
std::memcpy(buffer, &_header, sizeof(_header));
|
||||||
buffer += sizeof(_header);
|
buffer += sizeof(_header);
|
||||||
total_sz -= sizeof(_header);
|
total_sz -= sizeof(_header);
|
||||||
@@ -225,6 +268,30 @@ void ICMPv6::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *
|
|||||||
#endif
|
#endif
|
||||||
buffer = write_option(*it, buffer);
|
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);
|
const Tins::IPv6 *ipv6 = tins_cast<const Tins::IPv6*>(parent);
|
||||||
if(ipv6) {
|
if(ipv6) {
|
||||||
uint32_t checksum = Utils::pseudoheader_checksum(
|
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);
|
buffer.push_back(0);
|
||||||
}
|
}
|
||||||
uint8_t padding = 8 - (buffer.size() + 2) % 8;
|
uint8_t padding = 8 - (buffer.size() + 2) % 8;
|
||||||
if(padding == 8)
|
if (padding == 8) {
|
||||||
padding = 0;
|
padding = 0;
|
||||||
|
}
|
||||||
buffer.insert(buffer.end(), padding, 0);
|
buffer.insert(buffer.end(), padding, 0);
|
||||||
add_option(option(DNS_SEARCH_LIST, buffer.begin(), buffer.end()));
|
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
|
// Option getters
|
||||||
// ********************************************************************
|
// ********************************************************************
|
||||||
|
|||||||
@@ -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) {
|
bool increment(IPv4Address &addr) {
|
||||||
uint32_t addr_int = Endian::be_to_host<uint32_t>(addr);
|
uint32_t addr_int = Endian::be_to_host<uint32_t>(addr);
|
||||||
bool reached_end = ++addr_int == 0xffffffff;
|
bool reached_end = ++addr_int == 0xffffffff;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "ip.h"
|
#include "ip.h"
|
||||||
#include "tcp.h"
|
#include "tcp.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
#include "rawpdu.h"
|
||||||
#include "hw_address.h"
|
#include "hw_address.h"
|
||||||
|
|
||||||
using namespace Tins;
|
using namespace Tins;
|
||||||
@@ -16,6 +17,8 @@ public:
|
|||||||
static const uint8_t expected_packet[];
|
static const uint8_t expected_packet[];
|
||||||
static const uint8_t expected_packet1[];
|
static const uint8_t expected_packet1[];
|
||||||
static const uint8_t expected_packet2[];
|
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);
|
void test_equals(const ICMPv6 &icmp1, const ICMPv6 &icmp2);
|
||||||
};
|
};
|
||||||
@@ -42,6 +45,24 @@ const uint8_t ICMPv6Test::expected_packet2[] = {
|
|||||||
128, 218
|
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) {
|
TEST_F(ICMPv6Test, Constructor) {
|
||||||
ICMPv6 icmp;
|
ICMPv6 icmp;
|
||||||
EXPECT_EQ(icmp.type(), ICMPv6::ECHO_REQUEST);
|
EXPECT_EQ(icmp.type(), ICMPv6::ECHO_REQUEST);
|
||||||
@@ -486,3 +507,40 @@ TEST_F(ICMPv6Test, RemoveOption) {
|
|||||||
PDU::serialization_type new_buffer = icmp.serialize();
|
PDU::serialization_type new_buffer = icmp.serialize();
|
||||||
EXPECT_EQ(old_buffer, new_buffer);
|
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()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -785,6 +785,17 @@ TEST_F(IPTest, FragmentOffset) {
|
|||||||
EXPECT_FALSE(ip.is_fragmented());
|
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
|
// Use a large buffer. This wil set the length field
|
||||||
TEST_F(IPTest, SerializePacketHavingICMPExtensionsWithLengthAndLotsOfPayload) {
|
TEST_F(IPTest, SerializePacketHavingICMPExtensionsWithLengthAndLotsOfPayload) {
|
||||||
IP encapsulated = IP(TINS_DEFAULT_TEST_IP) / UDP(99, 12) / RawPDU(std::string(250, 'A'));
|
IP encapsulated = IP(TINS_DEFAULT_TEST_IP) / UDP(99, 12) / RawPDU(std::string(250, 'A'));
|
||||||
|
|||||||
@@ -7,12 +7,20 @@
|
|||||||
#include "udp.h"
|
#include "udp.h"
|
||||||
#include "icmp.h"
|
#include "icmp.h"
|
||||||
#include "icmpv6.h"
|
#include "icmpv6.h"
|
||||||
|
#include "rawpdu.h"
|
||||||
|
#include "ethernetII.h"
|
||||||
#include "ipv6_address.h"
|
#include "ipv6_address.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace Tins;
|
using namespace Tins;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define TINS_DEFAULT_TEST_IP "::"
|
||||||
|
#else
|
||||||
|
#define TINS_DEFAULT_TEST_IP "::1"
|
||||||
|
#endif
|
||||||
|
|
||||||
class IPv6Test : public testing::Test {
|
class IPv6Test : public testing::Test {
|
||||||
public:
|
public:
|
||||||
static const uint8_t expected_packet1[], expected_packet2[];
|
static const uint8_t expected_packet1[], expected_packet2[];
|
||||||
@@ -175,3 +183,63 @@ TEST_F(IPv6Test, DestinationAddress) {
|
|||||||
EXPECT_EQ(ipv6.dst_addr(), "99af:1293::1");
|
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());
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user