1
0
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:
Matias Fontanini
2012-12-04 23:15:38 -03:00
parent 356fe00aad
commit 765285c6ee
3 changed files with 449 additions and 0 deletions

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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()));
}