1
0
mirror of https://github.com/mfontanini/libtins synced 2026-01-23 02:35:57 +01:00

IP now uses PDUOption::to<>. Started porting ICMPv6 to do so as well.

This commit is contained in:
Matias Fontanini
2013-12-16 15:50:17 -03:00
parent 112a357726
commit ea6638c163
8 changed files with 245 additions and 118 deletions

View File

@@ -110,6 +110,11 @@ namespace Endian {
template<size_t>
struct conversion_dispatcher;
template<>
struct conversion_dispatcher<sizeof(uint8_t)>
: public conversion_dispatch_helper<uint8_t>
{ };
template<>
struct conversion_dispatcher<sizeof(uint16_t)>
: public conversion_dispatch_helper<uint16_t>

View File

@@ -145,30 +145,53 @@ public:
/**
* \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;
typedef std::vector<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;
struct addr_list_type {
typedef std::vector<ipaddress_type> addresses_type;
uint8_t reserved[6];
addresses_type addresses;
addr_list_type(const addresses_type &addresses = addresses_type())
: addresses(addresses)
{
std::fill(reserved, reserved + sizeof(reserved), 0);
}
static addr_list_type from_option(const option &opt);
};
/**
* The type used to store the nonce option data.
*/
typedef std::vector<uint8_t> nonce_type;
/**
* The type used to store the MTU option.
*/
typedef std::pair<uint16_t, uint32_t> mtu_type;
/**
* \brief The type used to store the neighbour advertisement
* acknowledgement option data.
*
* The first member contains the option code field, while
* the second one contains the status.
*/
typedef std::pair<uint8_t, uint8_t> naack_type;
struct naack_type {
uint8_t code, status;
uint8_t reserved[4];
naack_type(uint8_t code = 0, uint8_t status = 0)
: code(code), status(status)
{
std::fill(reserved, reserved + 4, 0);
}
static naack_type from_option(const option &opt);
};
/**
* \brief The type used to store the link layer address option data.
@@ -207,6 +230,8 @@ public:
{
}
static lladdr_type from_option(const option &opt);
};
/**
@@ -226,6 +251,8 @@ public:
: prefix_len(prefix_len), A(A), L(L),
valid_lifetime(valid_lifetime), preferred_lifetime(preferred_lifetime),
prefix(prefix) { }
static prefix_info_type from_option(const option &opt);
};
/**
@@ -832,7 +859,7 @@ public:
*
* \param value The MTU option data.
*/
void mtu(uint32_t value);
void mtu(const mtu_type& value);
/**
* \brief Setter for the shortcut limit option.
@@ -1009,7 +1036,7 @@ public:
* This method will throw an option_not_found exception if the
* option is not found.
*/
uint32_t mtu() const;
mtu_type mtu() const;
/**
* \brief Getter for the shortcut limit option.

View File

@@ -184,6 +184,8 @@ namespace Tins {
: security(sec), compartments(comp),
handling_restrictions(hand_res), transmission_control(tcc)
{}
static security_type from_option(const option &opt);
};
/**
@@ -198,6 +200,8 @@ namespace Tins {
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);
};
/**

View File

@@ -37,6 +37,8 @@
#include "exceptions.h"
#include "endianness.h"
#include "internals.h"
#include "ip_address.h"
#include "ipv6_address.h"
namespace Tins {
/**
@@ -107,35 +109,73 @@ namespace Internals {
if(opt.data_size() % sizeof(T) != 0)
throw malformed_option();
const T *ptr = (const T*)opt.data_ptr();
const T *end = ptr + (opt.data_size() / sizeof(T));
const T *end = (const T*)(opt.data_ptr() + opt.data_size());
std::vector<T> output(std::distance(ptr, end));
typename std::vector<T>::iterator it = output.begin();
while(ptr < end) {
if(PDUType::endianness == PDUType::BE)
*it++ = Endian::be_to_host(*(ptr++));
*it++ = Endian::be_to_host(*ptr++);
else
*it++ = Endian::le_to_host(*(ptr++));
*it++ = Endian::le_to_host(*ptr++);
}
return output;
}
};
template<typename T>
struct converter<std::pair<T, T>, typename enable_if<is_unsigned_integral<T>::value>::type> {
template<>
struct converter<std::vector<IPv4Address> > {
template<typename X, typename PDUType>
static std::pair<T, T> convert(const PDUOption<X, PDUType>& opt) {
if(opt.data_size() != sizeof(T) * 2)
static std::vector<IPv4Address> convert(const PDUOption<X, PDUType>& opt) {
if(opt.data_size() % 4 != 0)
throw malformed_option();
const T *ptr = (const T*)opt.data_ptr();
std::pair<T, T> output;
const uint32_t *ptr = (const uint32_t*)opt.data_ptr();
const uint32_t *end = (const uint32_t*)(opt.data_ptr() + opt.data_size());
std::vector<IPv4Address> output(std::distance(ptr, end));
std::vector<IPv4Address>::iterator it = output.begin();
while(ptr < end) {
if(PDUType::endianness == PDUType::BE)
*it++ = IPv4Address(*ptr++);
else
*it++ = Endian::change_endian(*ptr++);
}
return output;
}
};
template<>
struct converter<std::vector<IPv6Address> > {
template<typename X, typename PDUType>
static std::vector<IPv6Address> convert(const PDUOption<X, PDUType>& opt) {
if(opt.data_size() % IPv6Address::address_size != 0)
throw malformed_option();
const uint8_t *ptr = opt.data_ptr(), *end = opt.data_ptr() + opt.data_size();
std::vector<IPv6Address> output;
while(ptr < end) {
output.push_back(IPv6Address(ptr));
ptr += IPv6Address::address_size;
}
return output;
}
};
template<typename T, typename U>
struct converter<std::pair<T, U>, typename enable_if<is_unsigned_integral<T>::value>::type> {
template<typename X, typename PDUType>
static std::pair<T, U> convert(const PDUOption<X, PDUType>& opt) {
if(opt.data_size() != sizeof(T) + sizeof(U))
throw malformed_option();
std::pair<T, U> output;
output.first = *(const T*)opt.data_ptr();
output.second = *(const U*)(opt.data_ptr() + sizeof(T));
if(PDUType::endianness == PDUType::BE) {
output.first = Endian::be_to_host(*ptr++);
output.second = Endian::be_to_host(*ptr);
output.first = Endian::be_to_host(output.first);
output.second = Endian::be_to_host(output.second);
}
else {
output.first = Endian::le_to_host(*ptr++);
output.second = Endian::le_to_host(*ptr);
output.first = Endian::le_to_host(output.first);
output.second = Endian::le_to_host(output.second);
}
return output;
}

View File

@@ -495,9 +495,6 @@ namespace Tins {
if(!opt)
throw option_not_found();
return opt->to<T>();
/*if(option && option->data_size() == sizeof(T))
return *(const T*)(&option->data_ptr()[0]);
throw option_not_found();*/
}
void internal_add_option(const option &option);

View File

@@ -305,9 +305,10 @@ void ICMPv6::redirect_header(PDU::serialization_type data) {
add_option(option(REDIRECT_HEADER, data.begin(), data.end()));
}
void ICMPv6::mtu(uint32_t value) {
void ICMPv6::mtu(const mtu_type& value) {
uint8_t buffer[sizeof(uint16_t) + sizeof(uint32_t)] = {0};
*((uint32_t*)(buffer + sizeof(uint16_t))) = Endian::host_to_be(value);
*(uint32_t*)buffer = Endian::host_to_be(value.first);
*(uint32_t*)(buffer + sizeof(uint16_t)) = Endian::host_to_be(value.second);
add_option(option(MTU, sizeof(buffer), buffer));
}
@@ -324,9 +325,12 @@ void ICMPv6::new_advert_interval(uint32_t value) {
}
void ICMPv6::new_home_agent_info(const new_ha_info_type &value) {
if(value.size() != 3)
throw malformed_option();
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);
*((uint16_t*)(buffer + sizeof(uint16_t))) = Endian::host_to_be(value[0]);
*((uint16_t*)(buffer + sizeof(uint16_t))) = Endian::host_to_be(value[1]);
*((uint16_t*)(buffer + sizeof(uint16_t) * 2)) = Endian::host_to_be(value[2]);
add_option(option(HOME_AGENT_INFO, sizeof(buffer), buffer));
}
@@ -339,10 +343,12 @@ void ICMPv6::target_addr_list(const addr_list_type &value) {
}
void ICMPv6::add_addr_list(uint8_t type, const addr_list_type &value) {
typedef addr_list_type::addresses_type::const_iterator iterator;
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.reserve(value.addresses.size() + 6);
buffer.insert(buffer.end(), value.reserved, value.reserved + 6);
for(iterator it = value.addresses.begin(); it != value.addresses.end(); ++it)
buffer.insert(buffer.end(), it->begin(), it->end());
add_option(option(type, buffer.begin(), buffer.end()));
}
@@ -394,8 +400,9 @@ void ICMPv6::link_layer_addr(lladdr_type value) {
void ICMPv6::naack(const naack_type &value) {
uint8_t buffer[6];
buffer[0] = value.first;
buffer[1] = value.second;
buffer[0] = value.code;
buffer[1] = value.status;
std::copy(value.reserved, value.reserved + 4, buffer + 2);
add_option(option(NAACK, buffer, buffer + sizeof(buffer)));
}
@@ -551,18 +558,9 @@ ICMPv6::hwaddress_type ICMPv6::target_link_layer_addr() const {
ICMPv6::prefix_info_type ICMPv6::prefix_info() const {
const option *opt = search_option(PREFIX_INFO);
if(!opt || opt->data_size() != 2 + sizeof(uint32_t) * 3 + ipaddress_type::address_size)
if(!opt)
throw option_not_found();
const uint8_t *ptr = opt->data_ptr();
prefix_info_type output;
output.prefix_len = *ptr++;
output.L = (*ptr >> 7) & 0x1;
output.A = (*ptr++ >> 6) & 0x1;
output.valid_lifetime = Endian::be_to_host(*(uint32_t*)ptr);
ptr += sizeof(uint32_t);
output.preferred_lifetime = Endian::be_to_host(*(uint32_t*)ptr);
output.prefix = ptr + sizeof(uint32_t) * 2;
return output;
return opt->to<prefix_info_type>();
}
PDU::serialization_type ICMPv6::redirect_header() const {
@@ -573,11 +571,11 @@ PDU::serialization_type ICMPv6::redirect_header() const {
return serialization_type(ptr, ptr + opt->data_size() - 6);
}
uint32_t ICMPv6::mtu() const {
ICMPv6::mtu_type ICMPv6::mtu() const {
const option *opt = search_option(MTU);
if(!opt || opt->data_size() != sizeof(uint16_t) + sizeof(uint32_t))
if(!opt)
throw option_not_found();
return Endian::be_to_host(*(const uint32_t*)(opt->data_ptr() + sizeof(uint16_t)));
return opt->to<mtu_type>();
}
uint8_t ICMPv6::shortcut_limit() const {
@@ -596,12 +594,9 @@ uint32_t ICMPv6::new_advert_interval() const {
ICMPv6::new_ha_info_type ICMPv6::new_home_agent_info() const {
const option *opt = search_option(HOME_AGENT_INFO);
if(!opt || opt->data_size() != sizeof(uint16_t) + sizeof(uint32_t))
if(!opt)
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))
);
return opt->to<new_ha_info_type>();
}
ICMPv6::addr_list_type ICMPv6::source_addr_list() const {
@@ -614,17 +609,9 @@ ICMPv6::addr_list_type ICMPv6::target_addr_list() const {
ICMPv6::addr_list_type ICMPv6::search_addr_list(OptionTypes type) const {
const option *opt = search_option(type);
if(!opt || opt->data_size() < 6 + ipaddress_type::address_size)
if(!opt)
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;
return opt->to<addr_list_type>();
}
ICMPv6::rsa_sign_type ICMPv6::rsa_signature() const {
@@ -650,10 +637,10 @@ uint64_t ICMPv6::timestamp() const {
}
ICMPv6::nonce_type ICMPv6::nonce() const {
const option *opt = safe_search_option<std::equal_to>(
NONCE, 0
);
return nonce_type(opt->data_ptr(), opt->data_ptr() + opt->data_size());
const option *opt = search_option(NONCE);
if(!opt)
throw option_not_found();
return opt->to<nonce_type>();
}
ICMPv6::ip_prefix_type ICMPv6::ip_prefix() const {
@@ -671,22 +658,17 @@ ICMPv6::ip_prefix_type ICMPv6::ip_prefix() const {
}
ICMPv6::lladdr_type ICMPv6::link_layer_addr() const {
// at least the option_code and 1 byte from the link layer address
const option *opt = safe_search_option<std::less>(
LINK_ADDRESS, 2
);
const uint8_t *ptr = opt->data_ptr();
lladdr_type output(*ptr++);
output.address.assign(ptr, opt->data_ptr() + opt->data_size());
return output;
const option *opt = search_option(LINK_ADDRESS);
if(!opt)
throw option_not_found();
return opt->to<lladdr_type>();
}
ICMPv6::naack_type ICMPv6::naack() const {
const option *opt = safe_search_option<std::not_equal_to>(
NAACK, 6
);
const uint8_t *ptr = opt->data_ptr();
return naack_type(ptr[0], ptr[1]);
const option *opt = search_option(NAACK);
if(!opt)
throw option_not_found();
return opt->to<naack_type>();
}
ICMPv6::map_type ICMPv6::map() const {
@@ -815,5 +797,55 @@ ICMPv6::dns_search_list_type ICMPv6::dns_search_list() const {
}
return output;
}
// Options stuff
ICMPv6::addr_list_type ICMPv6::addr_list_type::from_option(const option &opt)
{
if(opt.data_size() < 6 + ipaddress_type::address_size || (opt.data_size() - 6) % ipaddress_type::address_size != 0)
throw malformed_option();
addr_list_type output;
const uint8_t *ptr = opt.data_ptr(), *end = opt.data_ptr() + opt.data_size();
std::copy(ptr, ptr + 6, output.reserved);
ptr += 6;
while(ptr < end) {
output.addresses.push_back(ICMPv6::ipaddress_type(ptr));
ptr += ICMPv6::ipaddress_type::address_size;
}
return output;
}
ICMPv6::naack_type ICMPv6::naack_type::from_option(const option &opt)
{
if(opt.data_size() != 6)
throw malformed_option();
return naack_type(*opt.data_ptr(), opt.data_ptr()[1]);
}
ICMPv6::lladdr_type ICMPv6::lladdr_type::from_option(const option &opt)
{
if(opt.data_size() < 2)
throw malformed_option();
const uint8_t *ptr = opt.data_ptr();
lladdr_type output(*ptr++);
output.address.assign(ptr, opt.data_ptr() + opt.data_size());
return output;
}
ICMPv6::prefix_info_type ICMPv6::prefix_info_type::from_option(const option &opt)
{
if(opt.data_size() != 2 + sizeof(uint32_t) * 3 + ICMPv6::ipaddress_type::address_size)
throw malformed_option();
const uint8_t *ptr = opt.data_ptr();
prefix_info_type output;
output.prefix_len = *ptr++;
output.L = (*ptr >> 7) & 0x1;
output.A = (*ptr++ >> 6) & 0x1;
output.valid_lifetime = Endian::be_to_host(*(uint32_t*)ptr);
ptr += sizeof(uint32_t);
output.preferred_lifetime = Endian::be_to_host(*(uint32_t*)ptr);
output.prefix = ptr + sizeof(uint32_t) * 2;
return output;
}
}

View File

@@ -271,40 +271,24 @@ void IP::add_route_option(option_identifier id, const generic_route_option_type
}
IP::generic_route_option_type IP::search_route_option(option_identifier id) const {
const option *option = search_option(id);
if(!option || option->data_size() < 1 + sizeof(uint32_t) ||
((option->data_size() - 1) % sizeof(uint32_t)) != 0)
const option *opt = search_option(id);
if(!opt)
throw option_not_found();
generic_route_option_type output;
output.pointer = *option->data_ptr();
const uint32_t *route = (const uint32_t*)(option->data_ptr() + 1),
*end = route + (option->data_size() - 1) / sizeof(uint32_t);
while(route < end)
output.routes.push_back(address_type(*route++));
return output;
return opt->to<generic_route_option_type>();
}
IP::security_type IP::security() const {
const option *option = search_option(130);
if(!option || option->data_size() < 9)
const option *opt = search_option(130);
if(!opt)
throw option_not_found();
security_type output;
const uint16_t *ptr = reinterpret_cast<const uint16_t*>(option->data_ptr());
output.security = Endian::be_to_host(*ptr++);
output.compartments = Endian::be_to_host(*ptr++);
output.handling_restrictions = Endian::be_to_host(*ptr++);
uint32_t tcc = option->data_ptr()[6];
tcc = (tcc << 8) | option->data_ptr()[7];
tcc = (tcc << 8) | option->data_ptr()[8];
output.transmission_control = tcc;
return output;
return opt->to<security_type>();
}
uint16_t IP::stream_identifier() const {
const option *option = search_option(136);
if(!option || option->data_size() != sizeof(uint16_t))
const option *opt = search_option(136);
if(!opt)
throw option_not_found();
return Endian::be_to_host(*(const uint16_t*)option->data_ptr());
return opt->to<uint16_t>();
}
void IP::add_option(const option &opt) {
@@ -454,4 +438,36 @@ bool IP::matches_response(const uint8_t *ptr, uint32_t total_sz) const {
}
return false;
}
// Option static constructors from options
IP::security_type IP::security_type::from_option(const option &opt)
{
if(opt.data_size() != 9)
throw malformed_option();
security_type output;
const uint16_t *ptr = reinterpret_cast<const uint16_t*>(opt.data_ptr());
output.security = Endian::be_to_host(*ptr++);
output.compartments = Endian::be_to_host(*ptr++);
output.handling_restrictions = Endian::be_to_host(*ptr++);
uint32_t tcc = opt.data_ptr()[6];
tcc = (tcc << 8) | opt.data_ptr()[7];
tcc = (tcc << 8) | opt.data_ptr()[8];
output.transmission_control = tcc;
return output;
}
IP::generic_route_option_type IP::generic_route_option_type::from_option(
const option &opt)
{
if(opt.data_size() < 1 + sizeof(uint32_t) || ((opt.data_size() - 1) % sizeof(uint32_t)) != 0)
throw malformed_option();
generic_route_option_type output;
output.pointer = *opt.data_ptr();
const uint32_t *route = (const uint32_t*)(opt.data_ptr() + 1),
*end = route + (opt.data_size() - 1) / sizeof(uint32_t);
while(route < end)
output.routes.push_back(address_type(*route++));
return output;
}
}

View File

@@ -215,8 +215,9 @@ TEST_F(ICMPv6Test, RedirectHeader) {
TEST_F(ICMPv6Test, MTU) {
ICMPv6 icmp;
icmp.mtu(0x9a8df7);
EXPECT_EQ(icmp.mtu(), 0x9a8df7U);
ICMPv6::mtu_type data(0, 0x9a8df7);
icmp.mtu(data);
EXPECT_EQ(data, icmp.mtu());
}
TEST_F(ICMPv6Test, ShortcutLimit) {
@@ -233,7 +234,10 @@ TEST_F(ICMPv6Test, NewAdvertisementInterval) {
TEST_F(ICMPv6Test, NewHomeAgentInformation) {
ICMPv6 icmp;
ICMPv6::new_ha_info_type data(0x92fa, 0xaab3);
ICMPv6::new_ha_info_type data;
data.push_back(0);
data.push_back(0x92fa);
data.push_back(0xaab3);
icmp.new_home_agent_info(data);
EXPECT_EQ(icmp.new_home_agent_info(), data);
}
@@ -241,19 +245,19 @@ TEST_F(ICMPv6Test, NewHomeAgentInformation) {
TEST_F(ICMPv6Test, SourceAddressList) {
ICMPv6 icmp;
ICMPv6::addr_list_type data;
data.push_back("827d:adae::1");
data.push_back("2929:1234:fefe::2");
data.addresses.push_back("827d:adae::1");
data.addresses.push_back("2929:1234:fefe::2");
icmp.source_addr_list(data);
EXPECT_EQ(icmp.source_addr_list(), data);
EXPECT_EQ(icmp.source_addr_list().addresses, data.addresses);
}
TEST_F(ICMPv6Test, TargetAddressList) {
ICMPv6 icmp;
ICMPv6::addr_list_type data;
data.push_back("827d:adae::1");
data.push_back("2929:1234:fefe::2");
data.addresses.push_back("827d:adae::1");
data.addresses.push_back("2929:1234:fefe::2");
icmp.target_addr_list(data);
EXPECT_EQ(icmp.target_addr_list(), data);
EXPECT_EQ(icmp.target_addr_list().addresses, data.addresses);
}
TEST_F(ICMPv6Test, RSASignature) {
@@ -316,9 +320,11 @@ TEST_F(ICMPv6Test, LinkLayerAddress) {
TEST_F(ICMPv6Test, NAACK) {
ICMPv6 icmp;
ICMPv6::naack_type data(0x92, 0xb3);
ICMPv6::naack_type data(0x92, 0xb3), result;
icmp.naack(data);
EXPECT_EQ(icmp.naack(), data);
result = icmp.naack();
EXPECT_EQ(result.code, data.code);
EXPECT_EQ(result.status, data.status);
}
TEST_F(ICMPv6Test, MAP) {