mirror of
https://github.com/mfontanini/libtins
synced 2026-01-23 02:35:57 +01:00
Added several ICMPv6 option getters/setters.
This commit is contained in:
234
include/icmpv6.h
234
include/icmpv6.h
@@ -31,6 +31,7 @@
|
||||
#define TINS_ICMPV6_H
|
||||
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include "macros.h"
|
||||
#include "pdu.h"
|
||||
#include "ipv6_address.h"
|
||||
@@ -148,6 +149,55 @@ public:
|
||||
*/
|
||||
typedef std::pair<uint16_t, uint16_t> new_ha_info_type;
|
||||
|
||||
/**
|
||||
* The type used to store the source/target address list options.
|
||||
*/
|
||||
typedef std::vector<ipaddress_type> addr_list_type;
|
||||
|
||||
/**
|
||||
* The type used to store the nonce option data.
|
||||
*/
|
||||
typedef std::vector<uint8_t> nonce_type;
|
||||
|
||||
/**
|
||||
* \brief The type used to store the link layer address option data.
|
||||
*/
|
||||
struct lladdr_type {
|
||||
typedef std::vector<uint8_t> address_type;
|
||||
|
||||
uint8_t option_code;
|
||||
address_type address;
|
||||
|
||||
/**
|
||||
* Constructor taking an option code and an address.
|
||||
*
|
||||
* \param option_code The option code.
|
||||
* \param address The address to be stored.
|
||||
*/
|
||||
lladdr_type(uint8_t option_code = 0,
|
||||
const address_type &address = address_type())
|
||||
: option_code(option_code), address(address)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Constructor taking an option code and hwaddress_type.
|
||||
*
|
||||
* This is a helper constructor, since it'll be common to use
|
||||
* hwaddress_type as the link layer address.
|
||||
*
|
||||
* \param option_code The option code.
|
||||
* \param address The address to be stored.
|
||||
*/
|
||||
lladdr_type(uint8_t option_code,
|
||||
const hwaddress_type &address)
|
||||
: option_code(option_code), address(address.begin(), address.end())
|
||||
{
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Type type used to store the prefix information option data.
|
||||
*/
|
||||
@@ -183,6 +233,83 @@ public:
|
||||
}
|
||||
} TINS_END_PACK;
|
||||
|
||||
/**
|
||||
* The type used to store the RSA signature option.
|
||||
*/
|
||||
struct rsa_sign_type {
|
||||
typedef std::vector<uint8_t> signature_type;
|
||||
|
||||
uint8_t key_hash[16];
|
||||
signature_type signature;
|
||||
|
||||
/**
|
||||
* \brief Constructs a rsa_sign_type object.
|
||||
*
|
||||
* The first parameter must be a random access iterator
|
||||
* which will be used to initialize the key_hash member.
|
||||
* It is assumed that std::distance(hash, end_of_hash) >= 16.
|
||||
*
|
||||
* The second and third arguments indicate the start and end of
|
||||
* the sequence which will be used to initialize the signature
|
||||
* member.
|
||||
*
|
||||
* \param hash A random access iterator used to initialize the
|
||||
* key_hash member.
|
||||
* \param start A forward iterator pointing to the start of the
|
||||
* sequence which will be used to initialize the signature member.
|
||||
* \param end A forward iterator pointing to the end of the
|
||||
* sequence used to initialize signature.
|
||||
*/
|
||||
template<typename RAIterator, typename ForwardIterator>
|
||||
rsa_sign_type(RAIterator hash, ForwardIterator start, ForwardIterator end)
|
||||
: signature(start, end)
|
||||
{
|
||||
std::copy(hash, hash + sizeof(key_hash), key_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Constructs a rsa_sign_type object.
|
||||
*
|
||||
* The first parameter must be a random access iterator
|
||||
* which will be used to initialize the key_hash member.
|
||||
* It is assumed that std::distance(hash, end_of_hash) >= 16.
|
||||
*
|
||||
*
|
||||
* \param hash A random access iterator used to initialize the
|
||||
* key_hash member.
|
||||
* \param sign The signature to be set.
|
||||
*/
|
||||
template<typename RAIterator>
|
||||
rsa_sign_type(RAIterator hash, const signature_type &sign)
|
||||
: signature(sign)
|
||||
{
|
||||
std::copy(hash, hash + sizeof(key_hash), key_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Default constructs a rsa_sign_type.
|
||||
*
|
||||
* The key_hash member will be 0-initialized.
|
||||
*/
|
||||
rsa_sign_type()
|
||||
{
|
||||
std::fill(key_hash, key_hash + sizeof(key_hash), 0);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The type used to store IP address/preffix option.
|
||||
*/
|
||||
struct ip_prefix_type {
|
||||
uint8_t option_code, prefix_len;
|
||||
ipaddress_type address;
|
||||
|
||||
ip_prefix_type(uint8_t option_code = 0, uint8_t prefix_len = 0,
|
||||
const ipaddress_type &address = ipaddress_type())
|
||||
: option_code(option_code), prefix_len(prefix_len), address(address)
|
||||
{}
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Constructs an ICMPv6 object.
|
||||
*
|
||||
@@ -582,6 +709,55 @@ public:
|
||||
*/
|
||||
void new_home_agent_info(const new_ha_info_type &value);
|
||||
|
||||
/**
|
||||
* \brief Setter for the new source address list option.
|
||||
*
|
||||
* \param value The new source address list option data.
|
||||
*/
|
||||
void source_addr_list(const addr_list_type &value);
|
||||
|
||||
/**
|
||||
* \brief Setter for the new target address list option.
|
||||
*
|
||||
* \param value The new target address list option data.
|
||||
*/
|
||||
void target_addr_list(const addr_list_type &value);
|
||||
|
||||
/**
|
||||
* \brief Setter for the new RSA signature option.
|
||||
*
|
||||
* \param value The new RSA signature option data.
|
||||
*/
|
||||
void rsa_signature(const rsa_sign_type &value);
|
||||
|
||||
/**
|
||||
* \brief Setter for the new timestamp option.
|
||||
*
|
||||
* \param value The new timestamp option data.
|
||||
*/
|
||||
void timestamp(uint64_t value);
|
||||
|
||||
/**
|
||||
* \brief Setter for the new nonce option.
|
||||
*
|
||||
* \param value The new nonce option data.
|
||||
*/
|
||||
void nonce(const nonce_type &value);
|
||||
|
||||
/**
|
||||
* \brief Setter for the new IP address/prefix option.
|
||||
*
|
||||
* \param value The new IP address/prefix option data.
|
||||
*/
|
||||
void ip_prefix(const ip_prefix_type &value);
|
||||
|
||||
/**
|
||||
* \brief Setter for the new link layer address option.
|
||||
*
|
||||
* \param value The new link layer address option data.
|
||||
*/
|
||||
void link_layer_addr(lladdr_type value);
|
||||
|
||||
// Option getters
|
||||
|
||||
/**
|
||||
@@ -647,6 +823,62 @@ public:
|
||||
* option is not found.
|
||||
*/
|
||||
new_ha_info_type new_home_agent_info() const;
|
||||
|
||||
/**
|
||||
* \brief Getter for the new source address list option.
|
||||
*
|
||||
* This method will throw an option_not_found exception if the
|
||||
* option is not found.
|
||||
*/
|
||||
addr_list_type source_addr_list() const;
|
||||
|
||||
/**
|
||||
* \brief Getter for the new target address list option.
|
||||
*
|
||||
* This method will throw an option_not_found exception if the
|
||||
* option is not found.
|
||||
*/
|
||||
addr_list_type target_addr_list() const;
|
||||
|
||||
/**
|
||||
* \brief Getter for the new RSA signature option.
|
||||
*
|
||||
* This method will throw an option_not_found exception if the
|
||||
* option is not found.
|
||||
*/
|
||||
rsa_sign_type rsa_signature() const;
|
||||
|
||||
/**
|
||||
* \brief Getter for the new timestamp option.
|
||||
*
|
||||
* This method will throw an option_not_found exception if the
|
||||
* option is not found.
|
||||
*/
|
||||
uint64_t timestamp() const;
|
||||
|
||||
/**
|
||||
* \brief Getter for the new nonce option.
|
||||
*
|
||||
* This method will throw an option_not_found exception if the
|
||||
* option is not found.
|
||||
*/
|
||||
nonce_type nonce() const;
|
||||
|
||||
/**
|
||||
* \brief Getter for the new IP address/prefix option.
|
||||
*
|
||||
* This method will throw an option_not_found exception if the
|
||||
* option is not found.
|
||||
*/
|
||||
ip_prefix_type ip_prefix() const;
|
||||
|
||||
/**
|
||||
* \brief Getter for the new link layer address option.
|
||||
*
|
||||
* This method will throw an option_not_found exception if the
|
||||
* option is not found.
|
||||
*/
|
||||
lladdr_type link_layer_addr() const;
|
||||
private:
|
||||
TINS_BEGIN_PACK
|
||||
struct icmp6hdr {
|
||||
@@ -697,6 +929,8 @@ private:
|
||||
bool has_options() const;
|
||||
uint8_t *write_option(const icmpv6_option &opt, uint8_t *buffer);
|
||||
void parse_options(const uint8_t *&buffer, uint32_t &total_sz);
|
||||
void add_addr_list(uint8_t type, const addr_list_type &value);
|
||||
addr_list_type search_addr_list(Options type) const;
|
||||
|
||||
|
||||
icmp6hdr _header;
|
||||
|
||||
139
src/icmpv6.cpp
139
src/icmpv6.cpp
@@ -289,6 +289,68 @@ void ICMPv6::new_home_agent_info(const new_ha_info_type &value) {
|
||||
add_option(icmpv6_option(HOME_AGENT_INFO, sizeof(buffer), buffer));
|
||||
}
|
||||
|
||||
void ICMPv6::source_addr_list(const addr_list_type &value) {
|
||||
add_addr_list(S_ADDRESS_LIST, value);
|
||||
}
|
||||
|
||||
void ICMPv6::target_addr_list(const addr_list_type &value) {
|
||||
add_addr_list(T_ADDRESS_LIST, value);
|
||||
}
|
||||
|
||||
void ICMPv6::add_addr_list(uint8_t type, const addr_list_type &value) {
|
||||
std::vector<uint8_t> buffer;
|
||||
buffer.reserve(value.size() + 6);
|
||||
buffer.insert(buffer.end(), 6, 0);
|
||||
for(addr_list_type::const_iterator it(value.begin()); it != value.end(); ++it)
|
||||
buffer.insert(buffer.end(), it->begin(), it->end());
|
||||
add_option(icmpv6_option(type, buffer.begin(), buffer.end()));
|
||||
}
|
||||
|
||||
void ICMPv6::rsa_signature(const rsa_sign_type &value) {
|
||||
uint32_t total_sz = 2 + sizeof(value.key_hash) + value.signature.size();
|
||||
uint8_t padding = 8 - total_sz % 8;
|
||||
if(padding == 8)
|
||||
padding = 0;
|
||||
std::vector<uint8_t> buffer;
|
||||
buffer.reserve(total_sz + padding);
|
||||
buffer.insert(buffer.end(), 2, 0);
|
||||
buffer.insert(buffer.end(), value.key_hash, value.key_hash + sizeof(value.key_hash));
|
||||
buffer.insert(buffer.end(), value.signature.begin(), value.signature.end());
|
||||
buffer.insert(buffer.end(), padding, 0);
|
||||
add_option(icmpv6_option(RSA_SIGN, buffer.begin(), buffer.end()));
|
||||
}
|
||||
|
||||
void ICMPv6::timestamp(uint64_t value) {
|
||||
std::vector<uint8_t> buffer(6 + sizeof(uint64_t));
|
||||
buffer.insert(buffer.begin(), 6, 0);
|
||||
*((uint64_t*)&buffer[6]) = Endian::host_to_be(value);
|
||||
add_option(icmpv6_option(TIMESTAMP, buffer.begin(), buffer.end()));
|
||||
}
|
||||
|
||||
void ICMPv6::nonce(const nonce_type &value) {
|
||||
add_option(icmpv6_option(NONCE, value.begin(), value.end()));
|
||||
}
|
||||
|
||||
void ICMPv6::ip_prefix(const ip_prefix_type &value) {
|
||||
std::vector<uint8_t> buffer;
|
||||
buffer.reserve(6 + ipaddress_type::address_size);
|
||||
buffer.push_back(value.option_code);
|
||||
buffer.push_back(value.prefix_len);
|
||||
// reserved
|
||||
buffer.insert(buffer.end(), sizeof(uint32_t), 0);
|
||||
buffer.insert(buffer.end(), value.address.begin(), value.address.end());
|
||||
add_option(icmpv6_option(IP_PREFIX, buffer.begin(), buffer.end()));
|
||||
}
|
||||
|
||||
void ICMPv6::link_layer_addr(lladdr_type value) {
|
||||
value.address.insert(value.address.begin(), value.option_code);
|
||||
uint8_t padding = 8 - value.address.size() % 8;
|
||||
if(padding == 8)
|
||||
padding = 0;
|
||||
value.address.insert(value.address.end(), padding, 0);
|
||||
add_option(icmpv6_option(LINK_ADDRESS, value.address.begin(), value.address.end()));
|
||||
}
|
||||
|
||||
// Option getters
|
||||
|
||||
ICMPv6::hwaddress_type ICMPv6::source_link_layer_addr() const {
|
||||
@@ -354,5 +416,82 @@ ICMPv6::new_ha_info_type ICMPv6::new_home_agent_info() const {
|
||||
Endian::be_to_host(*(const uint16_t*)(opt->data_ptr() + sizeof(uint16_t) * 2))
|
||||
);
|
||||
}
|
||||
|
||||
ICMPv6::addr_list_type ICMPv6::source_addr_list() const {
|
||||
return search_addr_list(S_ADDRESS_LIST);
|
||||
}
|
||||
|
||||
ICMPv6::addr_list_type ICMPv6::target_addr_list() const {
|
||||
return search_addr_list(T_ADDRESS_LIST);
|
||||
}
|
||||
|
||||
ICMPv6::addr_list_type ICMPv6::search_addr_list(Options type) const {
|
||||
const icmpv6_option *opt = search_option(type);
|
||||
if(!opt || opt->data_size() < 6 + ipaddress_type::address_size)
|
||||
throw option_not_found();
|
||||
addr_list_type output;
|
||||
const uint8_t *ptr = opt->data_ptr() + 6, *end = opt->data_ptr() + opt->data_size();
|
||||
while(ptr < end) {
|
||||
if(ptr + ipaddress_type::address_size > end)
|
||||
throw option_not_found();
|
||||
output.push_back(ipaddress_type(ptr));
|
||||
ptr += ipaddress_type::address_size;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
ICMPv6::rsa_sign_type ICMPv6::rsa_signature() const {
|
||||
const icmpv6_option *opt = search_option(RSA_SIGN);
|
||||
// 2 bytes reserved + at least 1 byte signature.
|
||||
if(!opt || opt->data_size() < 2 + sizeof(rsa_sign_type::key_hash) + 1)
|
||||
throw option_not_found();
|
||||
const uint8_t *ptr = opt->data_ptr() + 2;
|
||||
rsa_sign_type output;
|
||||
std::copy(ptr, ptr + sizeof(output.key_hash), output.key_hash);
|
||||
ptr += sizeof(output.key_hash);
|
||||
output.signature.assign(ptr, opt->data_ptr() + opt->data_size());
|
||||
return output;
|
||||
}
|
||||
|
||||
uint64_t ICMPv6::timestamp() const {
|
||||
const icmpv6_option *opt = search_option(TIMESTAMP);
|
||||
// 6 bytes reserved
|
||||
if(!opt || opt->data_size() < 6 + sizeof(uint64_t))
|
||||
throw option_not_found();
|
||||
return Endian::be_to_host(*(uint64_t*)(opt->data_ptr() + 6));
|
||||
}
|
||||
|
||||
ICMPv6::nonce_type ICMPv6::nonce() const {
|
||||
const icmpv6_option *opt = search_option(NONCE);
|
||||
// at least a one byte nonce(though it should be 8byte-padded as per the RFC).
|
||||
if(!opt || opt->data_size() == 0)
|
||||
throw option_not_found();
|
||||
return nonce_type(opt->data_ptr(), opt->data_ptr() + opt->data_size());
|
||||
}
|
||||
|
||||
ICMPv6::ip_prefix_type ICMPv6::ip_prefix() const {
|
||||
const icmpv6_option *opt = search_option(IP_PREFIX);
|
||||
if(!opt || opt->data_size() != 6 + ipaddress_type::address_size)
|
||||
throw option_not_found();
|
||||
const uint8_t *ptr = opt->data_ptr();
|
||||
ip_prefix_type output;
|
||||
output.option_code = *ptr++;
|
||||
output.prefix_len = *ptr++;
|
||||
// skip padding
|
||||
ptr += sizeof(uint32_t);
|
||||
output.address = ipaddress_type(ptr);
|
||||
return output;
|
||||
}
|
||||
|
||||
ICMPv6::lladdr_type ICMPv6::link_layer_addr() const {
|
||||
const icmpv6_option *opt = search_option(LINK_ADDRESS);
|
||||
// at least the option_code and 1 byte from the link layer address
|
||||
if(!opt || opt->data_size() < 2)
|
||||
throw option_not_found();
|
||||
const uint8_t *ptr = opt->data_ptr();
|
||||
lladdr_type output(*ptr++);
|
||||
output.address.assign(ptr, opt->data_ptr() + opt->data_size());
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -240,3 +240,79 @@ TEST_F(ICMPv6Test, NewHomeAgentInformation) {
|
||||
icmp.new_home_agent_info(data);
|
||||
EXPECT_EQ(icmp.new_home_agent_info(), data);
|
||||
}
|
||||
|
||||
TEST_F(ICMPv6Test, SourceAddressList) {
|
||||
ICMPv6 icmp;
|
||||
ICMPv6::addr_list_type data;
|
||||
data.push_back("827d:adae::1");
|
||||
data.push_back("2929:1234:fefe::2");
|
||||
icmp.source_addr_list(data);
|
||||
EXPECT_EQ(icmp.source_addr_list(), data);
|
||||
}
|
||||
|
||||
TEST_F(ICMPv6Test, TargetAddressList) {
|
||||
ICMPv6 icmp;
|
||||
ICMPv6::addr_list_type data;
|
||||
data.push_back("827d:adae::1");
|
||||
data.push_back("2929:1234:fefe::2");
|
||||
icmp.target_addr_list(data);
|
||||
EXPECT_EQ(icmp.target_addr_list(), data);
|
||||
}
|
||||
|
||||
TEST_F(ICMPv6Test, RSASignature) {
|
||||
ICMPv6 icmp;
|
||||
ICMPv6::rsa_sign_type data, result;
|
||||
// can i haz std::iota?
|
||||
const uint8_t arr[16] = {
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16
|
||||
};
|
||||
std::copy(arr, arr + sizeof(arr), data.key_hash);
|
||||
data.signature.push_back(12);
|
||||
data.signature.push_back(15);
|
||||
data.signature.push_back(221);
|
||||
icmp.rsa_signature(data);
|
||||
result = icmp.rsa_signature();
|
||||
EXPECT_TRUE(std::equal(data.key_hash, data.key_hash + sizeof(data.key_hash), result.key_hash));
|
||||
// There might be some padding(in this case, there is).
|
||||
ASSERT_LE(data.signature.size(), result.signature.size());
|
||||
EXPECT_TRUE(std::equal(data.signature.begin(), data.signature.end(), result.signature.begin()));
|
||||
}
|
||||
|
||||
TEST_F(ICMPv6Test, Timestamp) {
|
||||
ICMPv6 icmp;
|
||||
icmp.timestamp(0x2837d6aaa231L);
|
||||
EXPECT_EQ(icmp.timestamp(), 0x2837d6aaa231L);
|
||||
}
|
||||
|
||||
TEST_F(ICMPv6Test, Nonce) {
|
||||
ICMPv6 icmp;
|
||||
ICMPv6::nonce_type data;
|
||||
data.push_back(22);
|
||||
data.push_back(211);
|
||||
data.push_back(67);
|
||||
icmp.nonce(data);
|
||||
EXPECT_EQ(icmp.nonce(), data);
|
||||
}
|
||||
|
||||
TEST_F(ICMPv6Test, IPPrefix) {
|
||||
ICMPv6 icmp;
|
||||
ICMPv6::ip_prefix_type data(67, 198, "ff00:0928:ddfa::"), output;
|
||||
icmp.ip_prefix(data);
|
||||
output = icmp.ip_prefix();
|
||||
EXPECT_EQ(output.option_code, data.option_code);
|
||||
EXPECT_EQ(output.prefix_len, data.prefix_len);
|
||||
EXPECT_EQ(output.address, data.address);
|
||||
}
|
||||
|
||||
TEST_F(ICMPv6Test, LinkLayerAddress) {
|
||||
ICMPv6 icmp;
|
||||
ICMPv6::lladdr_type data(67), output;
|
||||
data.address.push_back(87);
|
||||
data.address.push_back(22);
|
||||
data.address.push_back(185);
|
||||
icmp.link_layer_addr(data);
|
||||
output = icmp.link_layer_addr();
|
||||
EXPECT_EQ(output.option_code, data.option_code);
|
||||
ASSERT_LE(data.address.size(), output.address.size());
|
||||
EXPECT_TRUE(std::equal(data.address.begin(), data.address.end(), output.address.begin()));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user