mirror of
https://github.com/mfontanini/libtins
synced 2026-01-23 02:35:57 +01:00
Added some ICMPv6 option getters/setters.
This commit is contained in:
207
include/icmpv6.h
207
include/icmpv6.h
@@ -36,6 +36,7 @@
|
||||
#include "pdu_option.h"
|
||||
#include "endianness.h"
|
||||
#include "small_uint.h"
|
||||
#include "hw_address.h"
|
||||
|
||||
namespace Tins {
|
||||
/**
|
||||
@@ -120,7 +121,12 @@ public:
|
||||
/**
|
||||
* The type used to store addresses.
|
||||
*/
|
||||
typedef IPv6Address address_type;
|
||||
typedef IPv6Address ipaddress_type;
|
||||
|
||||
/**
|
||||
* The type used to store addresses.
|
||||
*/
|
||||
typedef HWAddress<6> hwaddress_type;
|
||||
|
||||
/**
|
||||
* The type used to represent ICMPv6 options.
|
||||
@@ -132,6 +138,49 @@ public:
|
||||
*/
|
||||
typedef std::list<icmpv6_option> options_type;
|
||||
|
||||
/**
|
||||
* \brief The type used to store the new home agent information
|
||||
* option data.
|
||||
*
|
||||
* The first member contains the home agent preference field, while
|
||||
* the second one contains the home agent lifetime.
|
||||
*/
|
||||
typedef std::pair<uint16_t, uint16_t> new_ha_info_type;
|
||||
|
||||
/**
|
||||
* Type type used to store the prefix information option data.
|
||||
*/
|
||||
struct prefix_info_type {
|
||||
uint8_t prefix_len;
|
||||
#if TINS_IS_LITTLE_ENDIAN
|
||||
uint8_t reserved1:6,
|
||||
A:1,
|
||||
L:1;
|
||||
#else
|
||||
uint8_t L:1,
|
||||
A:1,
|
||||
reserved1:6;
|
||||
#endif
|
||||
uint32_t valid_lifetime,
|
||||
preferred_lifetime,
|
||||
reserved2;
|
||||
uint8_t prefix[ipaddress_type::address_size];
|
||||
|
||||
prefix_info_type(uint8_t prefix_len=0, small_uint<1> A=0, small_uint<1> L=0,
|
||||
uint32_t valid_lifetime=0, uint32_t preferred_lifetime=0,
|
||||
const ipaddress_type &addr = ipaddress_type())
|
||||
: prefix_len(prefix_len), reserved1(0),
|
||||
#if TINS_IS_LITTLE_ENDIAN
|
||||
A(A), L(L),
|
||||
#else
|
||||
L(L), A(A),
|
||||
#endif
|
||||
valid_lifetime(valid_lifetime), preferred_lifetime(preferred_lifetime)
|
||||
{
|
||||
addr.copy(prefix);
|
||||
}
|
||||
} __attribute__((packed));
|
||||
|
||||
/**
|
||||
* \brief Constructs an ICMPv6 object.
|
||||
*
|
||||
@@ -285,9 +334,17 @@ public:
|
||||
* \brief Getter for the target address field.
|
||||
* \return The stored target address field value.
|
||||
*/
|
||||
const address_type &target_addr() const {
|
||||
const ipaddress_type &target_addr() const {
|
||||
return _target_address;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Getter for the destination address field.
|
||||
* \return The stored destination address field value.
|
||||
*/
|
||||
const ipaddress_type &dest_addr() const {
|
||||
return _dest_address;
|
||||
}
|
||||
|
||||
// Setters
|
||||
|
||||
@@ -379,7 +436,13 @@ public:
|
||||
* \brief Setter for the target address field.
|
||||
* \param new_target_addr The new target address field value.
|
||||
*/
|
||||
void target_addr(const address_type &new_target_addr);
|
||||
void target_addr(const ipaddress_type &new_target_addr);
|
||||
|
||||
/**
|
||||
* \brief Setter for the destination address field.
|
||||
* \param new_dest_addr The new destination address field value.
|
||||
*/
|
||||
void dest_addr(const ipaddress_type &new_dest_addr);
|
||||
|
||||
/**
|
||||
* \brief Setter for the reachable_time field.
|
||||
@@ -411,6 +474,15 @@ public:
|
||||
type() == REDIRECT;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Checks whether this ICMPv6 object has a target_addr field.
|
||||
*
|
||||
* This depends on the type field.
|
||||
*/
|
||||
bool has_dest_addr() const {
|
||||
return type() == REDIRECT;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Adds an ICMPv6 option.
|
||||
*
|
||||
@@ -446,6 +518,133 @@ public:
|
||||
ICMPv6 *clone() const {
|
||||
return new ICMPv6(*this);
|
||||
}
|
||||
|
||||
// Option setters
|
||||
|
||||
/**
|
||||
* \brief Setter for the source link layer address option.
|
||||
*
|
||||
* \param addr The source link layer address.
|
||||
*/
|
||||
void source_link_layer_addr(const hwaddress_type &addr);
|
||||
|
||||
/**
|
||||
* \brief Setter for the target link layer address option.
|
||||
*
|
||||
* \param addr The target link layer address.
|
||||
*/
|
||||
void target_link_layer_addr(const hwaddress_type &addr);
|
||||
|
||||
/**
|
||||
* \brief Setter for the prefix information option.
|
||||
*
|
||||
* \param info The prefix information.
|
||||
*/
|
||||
void prefix_info(prefix_info_type info);
|
||||
|
||||
/**
|
||||
* \brief Setter for the redirect header option.
|
||||
*
|
||||
* This method appends the 6 reserved bytes and inserts the
|
||||
* necessary padding at the end.
|
||||
*
|
||||
* \param data The redirect header option data.
|
||||
*/
|
||||
void redirect_header(PDU::serialization_type data);
|
||||
|
||||
/**
|
||||
* \brief Setter for the MTU option.
|
||||
*
|
||||
* \param value The MTU option data.
|
||||
*/
|
||||
void mtu(uint32_t value);
|
||||
|
||||
/**
|
||||
* \brief Setter for the shortcut limit option.
|
||||
*
|
||||
* \param value The shortcut limit option data.
|
||||
*/
|
||||
void shortcut_limit(uint8_t value);
|
||||
|
||||
/**
|
||||
* \brief Setter for the new advertisement interval option.
|
||||
*
|
||||
* \param value The new advertisement interval option data.
|
||||
*/
|
||||
void new_advert_interval(uint32_t value);
|
||||
|
||||
/**
|
||||
* \brief Setter for the new home agent information option.
|
||||
*
|
||||
* \param value The new home agent information option data.
|
||||
*/
|
||||
void new_home_agent_info(const new_ha_info_type &value);
|
||||
|
||||
// Option getters
|
||||
|
||||
/**
|
||||
* \brief Getter for the source link layer address option.
|
||||
*
|
||||
* This method will throw an option_not_found exception if the
|
||||
* option is not found.
|
||||
*/
|
||||
hwaddress_type source_link_layer_addr() const;
|
||||
|
||||
/**
|
||||
* \brief Getter for the target link layer address option.
|
||||
*
|
||||
* This method will throw an option_not_found exception if the
|
||||
* option is not found.
|
||||
*/
|
||||
hwaddress_type target_link_layer_addr() const;
|
||||
|
||||
/**
|
||||
* \brief Getter for the prefix information option.
|
||||
*
|
||||
* This method will throw an option_not_found exception if the
|
||||
* option is not found.
|
||||
*/
|
||||
prefix_info_type prefix_info() const;
|
||||
|
||||
/**
|
||||
* \brief Getter for the redirect header option.
|
||||
*
|
||||
* This method will throw an option_not_found exception if the
|
||||
* option is not found.
|
||||
*/
|
||||
PDU::serialization_type redirect_header() const;
|
||||
|
||||
/**
|
||||
* \brief Getter for the MTU option.
|
||||
*
|
||||
* This method will throw an option_not_found exception if the
|
||||
* option is not found.
|
||||
*/
|
||||
uint32_t mtu() const;
|
||||
|
||||
/**
|
||||
* \brief Getter for the shortcut limit option.
|
||||
*
|
||||
* This method will throw an option_not_found exception if the
|
||||
* option is not found.
|
||||
*/
|
||||
uint8_t shortcut_limit() const;
|
||||
|
||||
/**
|
||||
* \brief Getter for the new advertisement interval option.
|
||||
*
|
||||
* This method will throw an option_not_found exception if the
|
||||
* option is not found.
|
||||
*/
|
||||
uint32_t new_advert_interval() const;
|
||||
|
||||
/**
|
||||
* \brief Getter for the new home agent information option.
|
||||
*
|
||||
* This method will throw an option_not_found exception if the
|
||||
* option is not found.
|
||||
*/
|
||||
new_ha_info_type new_home_agent_info() const;
|
||||
private:
|
||||
struct icmp6hdr {
|
||||
uint8_t type;
|
||||
@@ -498,7 +697,7 @@ private:
|
||||
|
||||
|
||||
icmp6hdr _header;
|
||||
address_type _target_address;
|
||||
ipaddress_type _target_address, _dest_address;
|
||||
options_type _options;
|
||||
uint32_t _options_size;
|
||||
uint32_t reach_time, retrans_timer;
|
||||
|
||||
155
src/icmpv6.cpp
155
src/icmpv6.cpp
@@ -52,12 +52,19 @@ ICMPv6::ICMPv6(const uint8_t *buffer, uint32_t total_sz)
|
||||
std::memcpy(&_header, buffer, sizeof(_header));
|
||||
buffer += sizeof(_header);
|
||||
total_sz -= sizeof(_header);
|
||||
if(type() == NEIGHBOUR_SOLICIT || type() == NEIGHBOUR_ADVERT || type() == REDIRECT) {
|
||||
if(total_sz < address_type::address_size)
|
||||
if(has_target_addr()) {
|
||||
if(total_sz < ipaddress_type::address_size)
|
||||
throw std::runtime_error("Not enough size for the target address");
|
||||
target_addr(buffer);
|
||||
buffer += address_type::address_size;
|
||||
total_sz -= address_type::address_size;
|
||||
buffer += ipaddress_type::address_size;
|
||||
total_sz -= ipaddress_type::address_size;
|
||||
}
|
||||
if(has_dest_addr()) {
|
||||
if(total_sz < ipaddress_type::address_size)
|
||||
throw std::runtime_error("Not enough size for the destination address");
|
||||
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)
|
||||
@@ -150,12 +157,18 @@ void ICMPv6::retransmit_timer(uint32_t new_retrans_timer) {
|
||||
retrans_timer = Endian::host_to_be(new_retrans_timer);
|
||||
}
|
||||
|
||||
void ICMPv6::target_addr(const address_type &new_target_addr) {
|
||||
void ICMPv6::target_addr(const ipaddress_type &new_target_addr) {
|
||||
_target_address = new_target_addr;
|
||||
}
|
||||
|
||||
void ICMPv6::dest_addr(const ipaddress_type &new_dest_addr) {
|
||||
_dest_address = new_dest_addr;
|
||||
}
|
||||
|
||||
uint32_t ICMPv6::header_size() const {
|
||||
return sizeof(_header) + _options_size;
|
||||
return sizeof(_header) + _options_size +
|
||||
(has_target_addr() ? ipaddress_type::address_size : 0) +
|
||||
(has_dest_addr() ? ipaddress_type::address_size : 0);
|
||||
}
|
||||
|
||||
void ICMPv6::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *parent) {
|
||||
@@ -166,6 +179,14 @@ void ICMPv6::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *
|
||||
std::memcpy(buffer, &_header, sizeof(_header));
|
||||
buffer += sizeof(_header);
|
||||
total_sz -= sizeof(_header);
|
||||
if(has_target_addr()) {
|
||||
buffer = _target_address.copy(buffer);
|
||||
total_sz -= sizeof(ipaddress_type::address_size);
|
||||
}
|
||||
if(has_dest_addr()) {
|
||||
buffer = _dest_address.copy(buffer);
|
||||
total_sz -= sizeof(ipaddress_type::address_size);
|
||||
}
|
||||
for(options_type::const_iterator it = _options.begin(); it != _options.end(); ++it) {
|
||||
#ifdef TINS_DEBUG
|
||||
assert(total_sz >= it->data_size() + sizeof(uint8_t) * 2);
|
||||
@@ -202,7 +223,7 @@ void ICMPv6::add_option(const icmpv6_option &option) {
|
||||
|
||||
uint8_t *ICMPv6::write_option(const icmpv6_option &opt, uint8_t *buffer) {
|
||||
*buffer++ = opt.option();
|
||||
*buffer++ = opt.data_size();
|
||||
*buffer++ = (opt.data_size() + sizeof(uint8_t) * 2) / 8;
|
||||
return std::copy(opt.data_ptr(), opt.data_ptr() + opt.data_size(), buffer);
|
||||
}
|
||||
|
||||
@@ -213,5 +234,125 @@ const ICMPv6::icmpv6_option *ICMPv6::search_option(Options id) const {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Option setters
|
||||
|
||||
void ICMPv6::source_link_layer_addr(const hwaddress_type &addr) {
|
||||
add_option(icmpv6_option(SOURCE_ADDRESS, addr.begin(), addr.end()));
|
||||
}
|
||||
|
||||
void ICMPv6::target_link_layer_addr(const hwaddress_type &addr) {
|
||||
add_option(icmpv6_option(TARGET_ADDRESS, addr.begin(), addr.end()));
|
||||
}
|
||||
|
||||
void ICMPv6::prefix_info(prefix_info_type info) {
|
||||
info.valid_lifetime = Endian::host_to_be(info.valid_lifetime);
|
||||
info.preferred_lifetime = Endian::host_to_be(info.preferred_lifetime);
|
||||
add_option(
|
||||
icmpv6_option(PREFIX_INFO, sizeof(prefix_info_type), (const uint8_t*)&info)
|
||||
);
|
||||
}
|
||||
|
||||
void ICMPv6::redirect_header(PDU::serialization_type data) {
|
||||
// Reserved fields
|
||||
data.insert(data.begin(), 6, 0);
|
||||
// Padding(if necessary)
|
||||
uint8_t padding = 8 - (data.size() + sizeof(uint8_t) * 2) % 8;
|
||||
if(padding == 8)
|
||||
padding = 0;
|
||||
data.insert(data.end(), padding, 0);
|
||||
add_option(icmpv6_option(REDIRECT_HEADER, data.begin(), data.end()));
|
||||
}
|
||||
|
||||
void ICMPv6::mtu(uint32_t value) {
|
||||
uint8_t buffer[sizeof(uint16_t) + sizeof(uint32_t)] = {0};
|
||||
*((uint32_t*)(buffer + sizeof(uint16_t))) = Endian::host_to_be(value);
|
||||
add_option(icmpv6_option(MTU, sizeof(buffer), buffer));
|
||||
}
|
||||
|
||||
void ICMPv6::shortcut_limit(uint8_t value) {
|
||||
uint8_t buffer[sizeof(uint16_t) + sizeof(uint32_t)] = {0};
|
||||
buffer[0] = value;
|
||||
add_option(icmpv6_option(NBMA_SHORT_LIMIT, sizeof(buffer), buffer));
|
||||
}
|
||||
|
||||
void ICMPv6::new_advert_interval(uint32_t value) {
|
||||
uint8_t buffer[sizeof(uint16_t) + sizeof(uint32_t)] = {0};
|
||||
*((uint32_t*)(buffer + sizeof(uint16_t))) = Endian::host_to_be(value);
|
||||
add_option(icmpv6_option(ADVERT_INTERVAL, sizeof(buffer), buffer));
|
||||
}
|
||||
|
||||
void ICMPv6::new_home_agent_info(const new_ha_info_type &value) {
|
||||
uint8_t buffer[sizeof(uint16_t) + sizeof(uint32_t)] = {0};
|
||||
*((uint16_t*)(buffer + sizeof(uint16_t))) = Endian::host_to_be(value.first);
|
||||
*((uint16_t*)(buffer + sizeof(uint16_t) * 2)) = Endian::host_to_be(value.second);
|
||||
add_option(icmpv6_option(HOME_AGENT_INFO, sizeof(buffer), buffer));
|
||||
}
|
||||
|
||||
// Option getters
|
||||
|
||||
ICMPv6::hwaddress_type ICMPv6::source_link_layer_addr() const {
|
||||
const icmpv6_option *opt = search_option(SOURCE_ADDRESS);
|
||||
if(!opt || opt->data_size() != hwaddress_type::address_size)
|
||||
throw option_not_found();
|
||||
return hwaddress_type(opt->data_ptr());
|
||||
}
|
||||
|
||||
ICMPv6::hwaddress_type ICMPv6::target_link_layer_addr() const {
|
||||
const icmpv6_option *opt = search_option(TARGET_ADDRESS);
|
||||
if(!opt || opt->data_size() != hwaddress_type::address_size)
|
||||
throw option_not_found();
|
||||
return hwaddress_type(opt->data_ptr());
|
||||
}
|
||||
|
||||
ICMPv6::prefix_info_type ICMPv6::prefix_info() const {
|
||||
const icmpv6_option *opt = search_option(PREFIX_INFO);
|
||||
if(!opt || opt->data_size() != sizeof(prefix_info_type))
|
||||
throw option_not_found();
|
||||
prefix_info_type output;
|
||||
std::memcpy(&output, opt->data_ptr(), sizeof(prefix_info_type));
|
||||
output.valid_lifetime = Endian::be_to_host(output.valid_lifetime);
|
||||
output.preferred_lifetime = Endian::be_to_host(output.preferred_lifetime);
|
||||
return output;
|
||||
}
|
||||
|
||||
PDU::serialization_type ICMPv6::redirect_header() const {
|
||||
const icmpv6_option *opt = search_option(REDIRECT_HEADER);
|
||||
if(!opt || opt->data_size() < 6)
|
||||
throw option_not_found();
|
||||
const uint8_t *ptr = opt->data_ptr() + 6;
|
||||
return serialization_type(ptr, ptr + opt->data_size() - 6);
|
||||
}
|
||||
|
||||
uint32_t ICMPv6::mtu() const {
|
||||
const icmpv6_option *opt = search_option(MTU);
|
||||
if(!opt || opt->data_size() != sizeof(uint16_t) + sizeof(uint32_t))
|
||||
throw option_not_found();
|
||||
return Endian::be_to_host(*(const uint32_t*)(opt->data_ptr() + sizeof(uint16_t)));
|
||||
}
|
||||
|
||||
uint8_t ICMPv6::shortcut_limit() const {
|
||||
const icmpv6_option *opt = search_option(NBMA_SHORT_LIMIT);
|
||||
if(!opt || opt->data_size() != sizeof(uint16_t) + sizeof(uint32_t))
|
||||
throw option_not_found();
|
||||
return *opt->data_ptr();
|
||||
}
|
||||
|
||||
uint32_t ICMPv6::new_advert_interval() const {
|
||||
const icmpv6_option *opt = search_option(ADVERT_INTERVAL);
|
||||
if(!opt || opt->data_size() != sizeof(uint16_t) + sizeof(uint32_t))
|
||||
throw option_not_found();
|
||||
return Endian::be_to_host(*(const uint32_t*)(opt->data_ptr() + sizeof(uint16_t)));
|
||||
}
|
||||
|
||||
ICMPv6::new_ha_info_type ICMPv6::new_home_agent_info() const {
|
||||
const icmpv6_option *opt = search_option(HOME_AGENT_INFO);
|
||||
if(!opt || opt->data_size() != sizeof(uint16_t) + sizeof(uint32_t))
|
||||
throw option_not_found();
|
||||
return std::make_pair(
|
||||
Endian::be_to_host(*(const uint16_t*)(opt->data_ptr() + sizeof(uint16_t))),
|
||||
Endian::be_to_host(*(const uint16_t*)(opt->data_ptr() + sizeof(uint16_t) * 2))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#include <string>
|
||||
#include <stdint.h>
|
||||
#include "icmpv6.h"
|
||||
#include "ip.h"
|
||||
#include "tcp.h"
|
||||
#include "utils.h"
|
||||
#include "hw_address.h"
|
||||
|
||||
@@ -178,3 +180,63 @@ TEST_F(ICMPv6Test, RTLifetime) {
|
||||
icmp.router_lifetime(0x827f);
|
||||
EXPECT_EQ(icmp.router_lifetime(), 0x827f);
|
||||
}
|
||||
|
||||
TEST_F(ICMPv6Test, SourceLinkLayerAddress) {
|
||||
ICMPv6 icmp;
|
||||
icmp.source_link_layer_addr("09:fe:da:fe:22:33");
|
||||
EXPECT_EQ(icmp.source_link_layer_addr(), "09:fe:da:fe:22:33");
|
||||
}
|
||||
|
||||
TEST_F(ICMPv6Test, TargetLinkLayerAddress) {
|
||||
ICMPv6 icmp;
|
||||
icmp.target_link_layer_addr("09:fe:da:fe:22:33");
|
||||
EXPECT_EQ(icmp.target_link_layer_addr(), "09:fe:da:fe:22:33");
|
||||
}
|
||||
|
||||
TEST_F(ICMPv6Test, PrefixInformation) {
|
||||
ICMPv6 icmp;
|
||||
ICMPv6::prefix_info_type result, info(0x8, 1, 0, 0x92038fad,
|
||||
0x918273fa, "827d:adae::1");
|
||||
icmp.prefix_info(info);
|
||||
result = icmp.prefix_info();
|
||||
EXPECT_EQ(result.prefix_len, info.prefix_len);
|
||||
EXPECT_EQ(result.A, info.A);
|
||||
EXPECT_EQ(result.L, info.L);
|
||||
EXPECT_EQ(result.valid_lifetime, info.valid_lifetime);
|
||||
EXPECT_EQ(result.preferred_lifetime, info.preferred_lifetime);
|
||||
EXPECT_EQ(IPv6Address(result.prefix), IPv6Address(result.prefix));
|
||||
EXPECT_EQ(IPv6Address(result.prefix), "827d:adae::1");
|
||||
}
|
||||
|
||||
TEST_F(ICMPv6Test, RedirectHeader) {
|
||||
ICMPv6 icmp;
|
||||
IP ip = IP("127.0.0.1") / TCP(22);
|
||||
PDU::serialization_type buffer = ip.serialize();
|
||||
icmp.redirect_header(buffer);
|
||||
EXPECT_EQ(buffer, icmp.redirect_header());
|
||||
}
|
||||
|
||||
TEST_F(ICMPv6Test, MTU) {
|
||||
ICMPv6 icmp;
|
||||
icmp.mtu(0x9a8df7);
|
||||
EXPECT_EQ(icmp.mtu(), 0x9a8df7);
|
||||
}
|
||||
|
||||
TEST_F(ICMPv6Test, ShortcutLimit) {
|
||||
ICMPv6 icmp;
|
||||
icmp.shortcut_limit(123);
|
||||
EXPECT_EQ(icmp.shortcut_limit(), 123);
|
||||
}
|
||||
|
||||
TEST_F(ICMPv6Test, NewAdvertisementInterval) {
|
||||
ICMPv6 icmp;
|
||||
icmp.new_advert_interval(0x9a8df7);
|
||||
EXPECT_EQ(icmp.new_advert_interval(), 0x9a8df7);
|
||||
}
|
||||
|
||||
TEST_F(ICMPv6Test, NewHomeAgentInformation) {
|
||||
ICMPv6 icmp;
|
||||
ICMPv6::new_ha_info_type data(0x92fa, 0xaab3);
|
||||
icmp.new_home_agent_info(data);
|
||||
EXPECT_EQ(icmp.new_home_agent_info(), data);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user