2024-02-21 14:52:47 +03:00

779 lines
21 KiB
C++

/*
* Copyright (c) 2017, Matias Fontanini
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef TINS_IP_H
#define TINS_IP_H
#include <tins/pdu.h>
#include <tins/small_uint.h>
#include <tins/endianness.h>
#include <tins/ip_address.h>
#include <tins/pdu_option.h>
#include <tins/macros.h>
#include <tins/cxxstd.h>
namespace Tins {
namespace Memory {
class OutputMemoryStream;
} // Memory
/**
* \class IP
* \brief Class that represents an IP PDU.
*
* By default, IP PDUs are initialized, setting TTL to IP::DEFAULT_TTL,
* id field to 1 and version to 4. Taking this into account, users
* should set destination and source port and would be enough to send one.
*
* When IP is the lowest layer on a packet, and the packet is serialized
* this willc heck if the source address is different than 0.0.0.0. If it is,
* the address of the interface in which the packet is going to be sent
* is retrieved (by using the routing table and the destination address)
* and set as the source address. If you don't want this behaviour, simply
* set the source address to 0.0.0.0.
*/
class TINS_API IP : public PDU {
public:
/**
* This PDU's flag.
*/
static const PDU::PDUType pdu_flag = PDU::IP;
/**
* The type used to store addresses.
*/
typedef IPv4Address address_type;
/**
* Type used to represent the different IP flags.
*/
enum Flags {
FLAG_RESERVED = 4,
DONT_FRAGMENT = 2,
MORE_FRAGMENTS = 1
};
/**
* \brief Enum indicating the option's class.
*
* Enum OptionClass represents the different classes of
* IP Options.
*/
enum OptionClass {
CONTROL = 0,
MEASUREMENT = 2
};
/**
* \brief Enum indicating the option's id number.
*
* Enum Option indicates the possible IP Options.
*/
enum OptionNumber {
END = 0,
NOOP = 1,
SEC = 2,
LSRR = 3,
TIMESTAMP = 4,
EXTSEC = 5,
RR = 7,
SID = 8,
SSRR = 9,
MTUPROBE = 11,
MTUREPLY = 12,
EIP = 17,
TR = 18,
ADDEXT = 19,
RTRALT = 20,
SDB = 21,
DPS = 23,
UMP = 24,
QS = 25
};
/**
* \brief The type used to represent an option's type.
*/
TINS_BEGIN_PACK
struct option_identifier {
#if TINS_IS_LITTLE_ENDIAN
uint8_t number:5,
op_class:2,
copied:1;
#elif TINS_IS_BIG_ENDIAN
uint8_t copied:1,
op_class:2,
number:5;
#endif
/**
* \brief Default constructor.
*
* Initializes every field to 0.
*/
option_identifier()
#if TINS_IS_LITTLE_ENDIAN
: number(0), op_class(0), copied(0) {}
#else
: copied(0), op_class(0), number(0) {}
#endif
/**
* \brief Constructs this option from a single uint8_t value.
*
* This parses the value and initializes each field with the
* appropriate value.
*
* \param value The value to be parsed and used for
* initialization
*/
option_identifier(uint8_t value)
#if TINS_IS_LITTLE_ENDIAN
: number(value & 0x1f),
op_class((value >> 5) & 0x03),
copied((value >> 7) & 0x01) {}
#elif TINS_IS_BIG_ENDIAN
: copied((value >> 7) & 0x01),
op_class((value >> 5) & 0x03),
number(value & 0x1f) {}
#endif
/**
* Constructor using user provided values for each field.
* \param number The number field value.
* \param op_class The option class field value.
* \param copied The copied field value.
*/
option_identifier(OptionNumber number, OptionClass op_class,
small_uint<1> copied)
#if TINS_IS_LITTLE_ENDIAN
: number(static_cast<uint8_t>(number)), op_class(static_cast<uint8_t>(op_class)), copied(copied) {}
#else
: copied(copied), op_class(static_cast<uint8_t>(op_class)), number(static_cast<uint8_t>(number)) {}
#endif
/**
* \brief Equality operator.
*/
bool operator==(const option_identifier& rhs) const {
return number == rhs.number && op_class == rhs.op_class && copied == rhs.copied;
}
} TINS_END_PACK;
/**
* The IP options type.
*/
typedef PDUOption<option_identifier, IP> option;
/**
* The type of the security option.
*/
struct security_type {
uint16_t security, compartments;
uint16_t handling_restrictions;
small_uint<24> transmission_control;
security_type(uint16_t sec = 0,
uint16_t comp = 0,
uint16_t hand_res = 0,
small_uint<24> tcc = 0)
: security(sec), compartments(comp),
handling_restrictions(hand_res), transmission_control(tcc) { }
static security_type from_option(const option& opt);
};
/**
* The type of the Loose Source and Record Route
*/
struct generic_route_option_type {
typedef std::vector<address_type> routes_type;
uint8_t pointer;
routes_type routes;
generic_route_option_type(uint8_t ptr = 0, routes_type rts = routes_type())
: pointer(ptr), routes(rts) {}
static generic_route_option_type from_option(const option& opt);
};
/**
* The type of the Loose Source and Record Route
*/
typedef generic_route_option_type lsrr_type;
/**
* The type of the Strict Source and Record Route
*/
typedef generic_route_option_type ssrr_type;
/**
* The type of the Record Route
*/
typedef generic_route_option_type record_route_type;
/**
* The type used to store IP options.
*/
typedef std::vector<option> options_type;
/**
* \brief Extracts metadata for this protocol based on the buffer provided
*
* \param buffer Pointer to a buffer
* \param total_sz Size of the buffer pointed by buffer
*/
static metadata extract_metadata(const uint8_t *buffer, uint32_t total_sz);
/**
* \brief Constructor for building the IP PDU.
*
* Both the destination and source IP address can be supplied.
* By default, those fields are initialized using the IP
* address 0.0.0.0.
*
* \param ip_dst The destination ip address(optional).
* \param ip_src The source ip address(optional).
*/
IP(address_type ip_dst = address_type(),
address_type ip_src = address_type());
/**
* \brief Constructs an IP object from a buffer and adds all
* identifiable PDUs found in the buffer as children of this
* one.
*
* If there is not enough size for an IP header, a
* malformed_packet exception is thrown.
*
* \param buffer The buffer from which this PDU will be constructed.
* \param total_sz The total size of the buffer.
*/
IP(const uint8_t* buffer, uint32_t total_sz);
/* Getters */
uint32_t advertised_size() const {
return static_cast<uint32_t>(tot_len());
}
/**
* \brief Getter for the header length field.
*
* \return The number of dwords the header occupies in an uin8_t.
*/
small_uint<4> head_len() const {
return this->header_.ihl;
}
/**
* \brief Getter for the type of service field.
*
* \return The this IP PDU's type of service.
*/
uint8_t tos() const {
return header_.tos;
}
/**
* \brief Getter for the total length field.
*
* \return The total length of this IP PDU.
*/
uint16_t tot_len() const {
return Endian::be_to_host(header_.tot_len);
}
/**
* \brief Getter for the id field.
*
* \return The id for this IP PDU.
*/
uint16_t id() const {
return Endian::be_to_host(header_.id);
}
/**
* \brief Getter for the fragment offset field.
*
* This method is deprecated. Use IP::fragment_offset and IP::flags.
*
* \deprecated
* \return The fragment offset for this IP PDU.
* \sa IP::fragment_offset
* \sa IP::flags
*/
TINS_DEPRECATED(uint16_t frag_off() const) {
return Endian::be_to_host(header_.frag_off);
}
/**
* \brief Getter for the fragment offset field.
*
* This will return the fragment offset field, as present in the packet,
* which indicates the offset of this fragment in blocks of 8 bytes.
*
* \return The fragment offset, measured in units of 8 byte blocks
*/
small_uint<13> fragment_offset() const {
return Endian::be_to_host(header_.frag_off) & 0x1fff;
}
/**
* \brief Getter for the flags field.
*
* \return The IP flags field
*/
Flags flags() const {
return static_cast<Flags>(Endian::be_to_host(header_.frag_off) >> 13);
}
/**
* \brief Getter for the time to live field.
*
* \return The time to live for this IP PDU.
*/
uint8_t ttl() const {
return header_.ttl;
}
/**
* \brief Getter for the protocol field.
*
* \return The protocol for this IP PDU.
*/
uint8_t protocol() const {
return header_.protocol;
}
/**
* \brief Getter for the checksum field.
*
* \return The checksum for this IP PDU.
*/
uint16_t checksum() const {
return Endian::be_to_host(header_.check);
}
/**
* \brief Getter for the source address field.
*
* \return The source address for this IP PDU.
*/
address_type src_addr() const {
return address_type(header_.saddr);
}
/**
* \brief Getter for the destination address field.
* \return The destination address for this IP PDU.
*/
address_type dst_addr() const {
return address_type(header_.daddr);
}
/**
* \brief Getter for the version field.
* \return The version for this IP PDU.
*/
small_uint<4> version() const {
return header_.version;
}
/**
* \brief Getter for the IP options.
* \return The stored options.
*/
const options_type& options() const {
return options_;
}
/* Setters */
/**
* \brief Setter for the type of service field.
*
* \param new_tos The new type of service.
*/
void tos(uint8_t new_tos);
/**
* \brief Setter for the id field.
*
* \param new_id The new id.
*/
void id(uint16_t new_id);
/**
* \brief Setter for the fragment offset field.
*
* This method is deprecated. Use IP::fragment_offset and IP::flags.
*
* \deprecated
* \param new_frag_off The new fragment offset.
* \sa IP::fragment_offset
* \sa IP::flags
*/
TINS_DEPRECATED(void frag_off(uint16_t new_frag_off));
/**
* \brief Setter for the fragment offset field.
*
* The value provided is measured in units of 8 byte blocks. This means that
* if you want this packet to have a fragment offset of <i>X</i>,
* you need to provide <i>X / 8</i> as the argument to this method.
*
* \param new_frag_off The new fragment offset, measured in units of 8 byte blocks.
*/
void fragment_offset(small_uint<13> new_frag_off);
/**
* \brief Setter for the flags field.
*
* \param new_flags The new IP flags field value.
*/
void flags(Flags new_flags);
/**
* \brief Setter for the time to live field.
*
* \param new_ttl The new time to live.
*/
void ttl(uint8_t new_ttl);
/**
* \brief Setter for the protocol field.
*
* Note that this protocol will be overwritten using the
* inner_pdu's protocol type during serialization unless the IP
* datagram is fragmented.
*
* If the packet is fragmented and was originally sniffed, the
* original protocol type will be kept when serialized.
*
* If this packet has been crafted manually and the inner_pdu
* is, for example, a RawPDU, then setting the protocol yourself
* is necessary.
*
* \param new_protocol The new protocol.
*/
void protocol(uint8_t new_protocol);
/**
* \brief Setter for the source address field.
*
* \param ip The source address to be set.
*/
void src_addr(address_type ip);
/**
* \brief Setter for the destination address field.
*
* \param ip The destination address to be set.
*/
void dst_addr(address_type ip);
/**
* \brief Setter for the version field.
*
* \param ver The version field to be set.
*/
void version(small_uint<4> ver);
/**
* \brief Adds an IP option.
*
* The option is added after the last option in the option
* fields.
*
* \param opt The option to be added
*/
void add_option(const option& opt);
#if TINS_IS_CXX11
/**
* \brief Adds an IP option.
*
* The option is move-constructed.
*
* \param opt The option to be added.
*/
void add_option(option &&opt) {
options_.push_back(std::move(opt));
}
/**
* \brief Adds an IP option.
*
* The option is constructed from the provided parameters.
*
* \param args The arguments to be used in the option's
* constructor.
*/
template<typename... Args>
void add_option(Args&&... args) {
options_.emplace_back(std::forward<Args>(args)...);
}
#endif
/**
* \brief Removes an IP option.
*
* If there are multiple options of the given type, only the first one
* will be removed.
*
* \param type The type of the option to be removed.
* \return true if the option was removed, false otherwise.
*/
bool remove_option(option_identifier id);
/**
* \brief Searchs for an option that matchs the given flag.
*
* If the option is not found, a null pointer is returned.
* Deleting the returned pointer will result in <b>undefined
* behaviour</b>.
*
* \param id The option identifier to be searched.
*/
const option* search_option(option_identifier id) const;
// Option setters
/**
* \brief Adds an End Of List option.
*/
void eol();
/**
* \brief Adds a NOP option.
*/
void noop();
/**
* \brief Adds a security option.
*
* \param data The data to be stored in this option.
*/
void security(const security_type& data);
/**
* \brief Adds a Loose Source and Record Route option.
*
* \param data The data to be stored in this option.
*/
void lsrr(const lsrr_type& data) {
add_route_option(131, data);
}
/**
* \brief Adds a Strict Source and Record Route option.
*
* \param data The data to be stored in this option.
*/
void ssrr(const ssrr_type& data) {
add_route_option(137, data);
}
/**
* \brief Adds a Record Route option.
*
* \param data The data to be stored in this option.
*/
void record_route(const record_route_type& data) {
add_route_option(7, data);
}
/**
* \brief Adds a Stream Identifier option.
*
* \param stream_id The stream id to be stored in this option.
*/
void stream_identifier(uint16_t stream_id);
// Option getters
/**
* \brief Searchs and returns a security option.
*
* If no such option exists, an option_not_found exception
* is thrown.
*
* \return security_type containing the option found.
*/
security_type security() const;
/**
* \brief Searchs and returns a Loose Source and Record Route
* option.
*
* If no such option exists, an option_not_found exception
* is thrown.
*
* \return lsrr_type containing the option found.
*/
lsrr_type lsrr() const {
return search_route_option(131);
}
/**
* \brief Searchs and returns a Strict Source and Record Route
* option.
*
* If no such option exists, an option_not_found exception
* is thrown.
*
* \return ssrr_type containing the option found.
*/
ssrr_type ssrr() const {
return search_route_option(137);
}
/**
* \brief Searchs and returns a Record Route option.
*
* If no such option exists, an option_not_found exception
* is thrown.
*
* \return record_route_type containing the option found.
*/
record_route_type record_route() const {
return search_route_option(7);
}
/**
* \brief Searchs and returns a Stream Identifier option.
*
* If no such option exists, an option_not_found exception
* is thrown.
*
* \return uint16_t containing the option found.
*/
uint16_t stream_identifier() const;
/* Virtual methods */
/**
* \brief Returns the header size.
*
* This method overrides PDU::header_size. \sa PDU::header_size
*/
uint32_t header_size() const;
/**
* \sa PDU::send()
*/
void send(PacketSender& sender, const NetworkInterface &);
/**
* \brief Check whether 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 Receives a matching response for this packet.
*
* \sa PDU::recv_response
* \param sender The packet sender which will receive the packet.
*/
PDU* recv_response(PacketSender& sender, const NetworkInterface &);
/**
* Indicates whether this PDU is fragmented.
*
* \return true if this PDU is fragmented, false otherwise.
*/
bool is_fragmented() const;
/**
* \brief Getter for the PDU's type.
* \sa PDU::pdu_type
*/
PDUType pdu_type() const {
return pdu_flag;
}
/**
* \sa PDU::clone
*/
IP* clone() const {
return new IP(*this);
}
private:
static const uint8_t DEFAULT_TTL;
TINS_BEGIN_PACK
struct ip_header {
#if TINS_IS_LITTLE_ENDIAN
uint8_t ihl:4,
version:4;
#else
uint8_t version:4,
ihl:4;
#endif
uint8_t tos;
uint16_t tot_len;
uint16_t id;
uint16_t frag_off;
uint8_t ttl;
uint8_t protocol;
uint16_t check;
uint32_t saddr;
uint32_t daddr;
} TINS_END_PACK;
void head_len(small_uint<4> new_head_len);
void tot_len(uint16_t new_tot_len);
void prepare_for_serialize();
uint32_t calculate_options_size() const;
uint32_t pad_options_size(uint32_t size) const;
void init_ip_fields();
void write_serialization(uint8_t* buffer, uint32_t total_sz);
void write_option(const option& opt, Memory::OutputMemoryStream& stream);
void add_route_option(option_identifier id, const generic_route_option_type& data);
generic_route_option_type search_route_option(option_identifier id) const;
void checksum(uint16_t new_check);
options_type::const_iterator search_option_iterator(option_identifier id) const;
options_type::iterator search_option_iterator(option_identifier id);
options_type options_;
ip_header header_;
};
} // Tins
#endif // TINS_IP_H