From 592a15ebe4736e58d43cf5ef6310bfcaf0702237 Mon Sep 17 00:00:00 2001 From: Matias Fontanini Date: Wed, 5 Dec 2012 23:33:09 -0300 Subject: [PATCH] Added some ICMPv6 option getters/setters. --- include/icmpv6.h | 137 +++++++++++++++++++++++++++++++++---------- src/icmpv6.cpp | 94 ++++++++++++++++++++++++++--- tests/src/icmpv6.cpp | 33 +++++++++++ 3 files changed, 225 insertions(+), 39 deletions(-) diff --git a/include/icmpv6.h b/include/icmpv6.h index c388a5b..64faf94 100644 --- a/include/icmpv6.h +++ b/include/icmpv6.h @@ -39,6 +39,7 @@ #include "endianness.h" #include "small_uint.h" #include "hw_address.h" +#include "small_uint.h" namespace Tins { /** @@ -102,7 +103,7 @@ public: IP_PREFIX, NEW_ROUTER_PREFIX, LINK_ADDRESS, - NEIGHBOUR_ADVERT_ACK, + NAACK, MAP = 23, ROUTE_INFO, RECURSIVE_DNS_SERV, @@ -159,6 +160,15 @@ public: */ typedef std::vector nonce_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 naack_type; + /** * \brief The type used to store the link layer address option data. */ @@ -201,37 +211,21 @@ public: /** * Type type used to store the prefix information option data. */ - TINS_BEGIN_PACK 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 + small_uint<1> A, L; uint32_t valid_lifetime, preferred_lifetime, reserved2; - uint8_t prefix[ipaddress_type::address_size]; + ipaddress_type prefix; 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); - } - } TINS_END_PACK; + uint32_t valid_lifetime=0, uint32_t preferred_lifetime=0, + const ipaddress_type &prefix = ipaddress_type()) + : prefix_len(prefix_len), A(A), L(L), + valid_lifetime(valid_lifetime), preferred_lifetime(preferred_lifetime), + prefix(prefix) { } + }; /** * The type used to store the RSA signature option. @@ -310,6 +304,39 @@ public: {} }; + /** + * The type used to store the map option. + */ + struct map_type { + small_uint<4> dist, pref; + small_uint<1> r; + uint32_t valid_lifetime; + ipaddress_type address; + + map_type(small_uint<4> dist = 0, small_uint<4> pref = 0, + small_uint<1> r = 0, uint32_t valid_lifetime = 0, + const ipaddress_type &address = ipaddress_type()) + : dist(dist), pref(pref), r(r), valid_lifetime(valid_lifetime), + address(address) { } + }; + + /** + * The type used to store the route information option. + */ + struct route_info_type { + typedef std::vector prefix_type; + + uint8_t prefix_len; + small_uint<2> pref; + uint32_t route_lifetime; + prefix_type prefix; + + route_info_type(uint8_t prefix_len = 0, small_uint<2> pref = 0, + uint32_t route_lifetime = 0, const prefix_type &prefix = prefix_type()) + : prefix_len(prefix_len), pref(pref), route_lifetime(route_lifetime), + prefix(prefix) { } + }; + /** * \brief Constructs an ICMPv6 object. * @@ -758,6 +785,27 @@ public: */ void link_layer_addr(lladdr_type value); + /** + * \brief Setter for the neighbour advertisement acknowledgement option. + * + * \param value The new naack option data. + */ + void naack(const naack_type &value); + + /** + * \brief Setter for the map option. + * + * \param value The new map option data. + */ + void map(const map_type &value); + + /** + * \brief Setter for the route information option. + * + * \param value The new route information option data. + */ + void route_info(const route_info_type &value); + // Option getters /** @@ -825,7 +873,7 @@ public: new_ha_info_type new_home_agent_info() const; /** - * \brief Getter for the new source address list option. + * \brief Getter for the source address list option. * * This method will throw an option_not_found exception if the * option is not found. @@ -833,7 +881,7 @@ public: addr_list_type source_addr_list() const; /** - * \brief Getter for the new target address list option. + * \brief Getter for the target address list option. * * This method will throw an option_not_found exception if the * option is not found. @@ -841,7 +889,7 @@ public: addr_list_type target_addr_list() const; /** - * \brief Getter for the new RSA signature option. + * \brief Getter for the RSA signature option. * * This method will throw an option_not_found exception if the * option is not found. @@ -849,7 +897,7 @@ public: rsa_sign_type rsa_signature() const; /** - * \brief Getter for the new timestamp option. + * \brief Getter for the timestamp option. * * This method will throw an option_not_found exception if the * option is not found. @@ -857,7 +905,7 @@ public: uint64_t timestamp() const; /** - * \brief Getter for the new nonce option. + * \brief Getter for the nonce option. * * This method will throw an option_not_found exception if the * option is not found. @@ -865,7 +913,7 @@ public: nonce_type nonce() const; /** - * \brief Getter for the new IP address/prefix option. + * \brief Getter for the IP address/prefix option. * * This method will throw an option_not_found exception if the * option is not found. @@ -873,12 +921,37 @@ public: ip_prefix_type ip_prefix() const; /** - * \brief Getter for the new link layer address option. + * \brief Getter for the 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; + + /** + * \brief Getter for the neighbour advertisement acknowledgement + * option. + * + * This method will throw an option_not_found exception if the + * option is not found. + */ + naack_type naack() const; + + /** + * \brief Getter for the map option. + * + * This method will throw an option_not_found exception if the + * option is not found. + */ + map_type map() const; + + /** + * \brief Getter for the map option. + * + * This method will throw an option_not_found exception if the + * option is not found. + */ + route_info_type route_info() const; private: TINS_BEGIN_PACK struct icmp6hdr { diff --git a/src/icmpv6.cpp b/src/icmpv6.cpp index a82b1bb..eefd6a6 100644 --- a/src/icmpv6.cpp +++ b/src/icmpv6.cpp @@ -246,10 +246,15 @@ void ICMPv6::target_link_layer_addr(const hwaddress_type &addr) { } 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); + uint8_t buffer[2 + sizeof(uint32_t) * 3 + ipaddress_type::address_size]; + buffer[0] = info.prefix_len; + buffer[1] = (info.L << 7) | (info.A << 6); + *(uint32_t*)(buffer + 2) = Endian::host_to_be(info.valid_lifetime); + *(uint32_t*)(buffer + 2 + sizeof(uint32_t)) = Endian::host_to_be(info.preferred_lifetime); + *(uint32_t*)(buffer + 2 + sizeof(uint32_t) * 2) = 0; + info.prefix.copy(buffer + 2 + sizeof(uint32_t) * 3); add_option( - icmpv6_option(PREFIX_INFO, sizeof(prefix_info_type), (const uint8_t*)&info) + icmpv6_option(PREFIX_INFO, buffer, buffer + sizeof(buffer)) ); } @@ -351,6 +356,39 @@ void ICMPv6::link_layer_addr(lladdr_type value) { add_option(icmpv6_option(LINK_ADDRESS, value.address.begin(), value.address.end())); } +void ICMPv6::naack(const naack_type &value) { + uint8_t buffer[6]; + buffer[0] = value.first; + buffer[1] = value.second; + add_option(icmpv6_option(NAACK, buffer, buffer + sizeof(buffer))); +} + +void ICMPv6::map(const map_type &value) { + uint8_t buffer[sizeof(uint8_t) * 2 + sizeof(uint32_t) + ipaddress_type::address_size]; + buffer[0] = value.dist << 4 | value.pref; + buffer[1] = value.r << 7; + *(uint32_t*)(buffer + 2) = Endian::host_to_be(value.valid_lifetime); + value.address.copy(buffer + 2 + sizeof(uint32_t)); + add_option(icmpv6_option(MAP, buffer, buffer + sizeof(buffer))); +} + +void ICMPv6::route_info(const route_info_type &value) { + uint8_t padding = 8 - value.prefix.size() % 8; + if(padding == 8) + padding = 0; + std::vector buffer(2 + sizeof(uint32_t) + value.prefix.size() + padding); + buffer[0] = value.prefix_len; + buffer[1] = value.pref << 3; + *(uint32_t*)&buffer[2] = Endian::host_to_be(value.route_lifetime); + // copy the prefix and then fill with padding + buffer.insert( + std::copy(value.prefix.begin(), value.prefix.end(), buffer.begin() + 2 + sizeof(uint32_t)), + padding, + 0 + ); + add_option(icmpv6_option(ROUTE_INFO, buffer.begin(), buffer.end())); +} + // Option getters ICMPv6::hwaddress_type ICMPv6::source_link_layer_addr() const { @@ -369,12 +407,17 @@ ICMPv6::hwaddress_type ICMPv6::target_link_layer_addr() const { 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)) + if(!opt || opt->data_size() != 2 + sizeof(uint32_t) * 3 + ipaddress_type::address_size) throw option_not_found(); + const uint8_t *ptr = opt->data_ptr(); 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); + 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; } @@ -493,5 +536,42 @@ ICMPv6::lladdr_type ICMPv6::link_layer_addr() const { output.address.assign(ptr, opt->data_ptr() + opt->data_size()); return output; } + +ICMPv6::naack_type ICMPv6::naack() const { + const icmpv6_option *opt = search_option(NAACK); + if(!opt || opt->data_size() != 6) + throw option_not_found(); + const uint8_t *ptr = opt->data_ptr(); + return naack_type(ptr[0], ptr[1]); +} + +ICMPv6::map_type ICMPv6::map() const { + const icmpv6_option *opt = search_option(MAP); + if(!opt || opt->data_size() != 2 + sizeof(uint32_t) + ipaddress_type::address_size) + throw option_not_found(); + const uint8_t *ptr = opt->data_ptr(); + map_type output; + output.dist = (*ptr >> 4) & 0x0f; + output.pref = *ptr++ & 0x0f; + output.r = (*ptr++ >> 7) & 0x01; + output.valid_lifetime = *(uint32_t*)ptr; + ptr += sizeof(uint32_t); + output.address = ptr; + return output; +} + +ICMPv6::route_info_type ICMPv6::route_info() const { + const icmpv6_option *opt = search_option(ROUTE_INFO); + if(!opt || opt->data_size() < 2 + sizeof(uint32_t)) + throw option_not_found(); + const uint8_t *ptr = opt->data_ptr(); + route_info_type output; + output.prefix_len = *ptr++; + output.pref = (*ptr++ >> 3) & 0x3; + output.route_lifetime = Endian::be_to_host(*(uint32_t*)ptr); + ptr += sizeof(uint32_t); + output.prefix.assign(ptr, opt->data_ptr() + opt->data_size()); + return output; +} } diff --git a/tests/src/icmpv6.cpp b/tests/src/icmpv6.cpp index d3e1ce8..0407f86 100644 --- a/tests/src/icmpv6.cpp +++ b/tests/src/icmpv6.cpp @@ -316,3 +316,36 @@ TEST_F(ICMPv6Test, LinkLayerAddress) { ASSERT_LE(data.address.size(), output.address.size()); EXPECT_TRUE(std::equal(data.address.begin(), data.address.end(), output.address.begin())); } + +TEST_F(ICMPv6Test, NAACK) { + ICMPv6 icmp; + ICMPv6::naack_type data(0x92, 0xb3); + icmp.naack(data); + EXPECT_EQ(icmp.naack(), data); +} + +TEST_F(ICMPv6Test, MAP) { + ICMPv6 icmp; + ICMPv6::map_type data(0x9, 0xb, 1, 0x9283719, "f029:adde::1"), output; + icmp.map(data); + output = icmp.map(); + EXPECT_EQ(output.dist, data.dist); + EXPECT_EQ(output.pref, data.pref); + EXPECT_EQ(output.r, data.r); + EXPECT_EQ(output.address, data.address); +} + +TEST_F(ICMPv6Test, RouteInfo) { + ICMPv6 icmp; + ICMPv6::route_info_type data(0x92, 2, 0xf23a8823), output; + data.prefix.push_back(98); + data.prefix.push_back(52); + data.prefix.push_back(44); + icmp.route_info(data); + output = icmp.route_info(); + EXPECT_EQ(output.prefix_len, data.prefix_len); + EXPECT_EQ(output.pref, data.pref); + EXPECT_EQ(output.route_lifetime, data.route_lifetime); + ASSERT_LE(data.prefix.size(), output.prefix.size()); + EXPECT_TRUE(std::equal(data.prefix.begin(), data.prefix.end(), output.prefix.begin())); +}