From 0945e0b29ee8acf10d8a0c0e99e38a816b400fe6 Mon Sep 17 00:00:00 2001 From: Matias Fontanini Date: Mon, 28 Jan 2013 16:23:47 -0300 Subject: [PATCH] Added DHCPv6 client/server id options' getters/setters. --- include/dhcpv6.h | 112 ++++++++++++++++++++++++++++++++++++ src/dhcpv6.cpp | 131 +++++++++++++++++++++++++++++++++++++++++++ src/loopback.cpp | 4 +- tests/src/dhcpv6.cpp | 62 ++++++++++++++++++++ 4 files changed, 307 insertions(+), 2 deletions(-) diff --git a/include/dhcpv6.h b/include/dhcpv6.h index e83ac15..0997bf1 100644 --- a/include/dhcpv6.h +++ b/include/dhcpv6.h @@ -352,6 +352,88 @@ public: : enterprise_number(enterprise_number), vendor_class_data(vendor_class_data) { } }; + + /** + * The type used to represent DUIDs Based on Link-layer Address Plus + * Time. + */ + struct duid_llt { + static const uint16_t duid_id = 1; + typedef std::vector lladdress_type; + + uint16_t hw_type; + uint32_t time; + lladdress_type lladdress; + + duid_llt(uint16_t hw_type = 0, uint32_t time = 0, + const lladdress_type &lladdress = lladdress_type()) + : hw_type(hw_type), time(time), lladdress(lladdress) {} + + PDU::serialization_type serialize() const; + + static duid_llt from_bytes(const uint8_t *buffer, uint32_t total_sz); + }; + + /** + * The type used to represent DUIDs Based on Enterprise Number + */ + struct duid_en { + static const uint16_t duid_id = 2; + typedef std::vector identifier_type; + + uint32_t enterprise_number; + identifier_type identifier; + + duid_en(uint32_t enterprise_number = 0, + const identifier_type &identifier = identifier_type()) + : enterprise_number(enterprise_number), identifier(identifier) {} + + PDU::serialization_type serialize() const; + + static duid_en from_bytes(const uint8_t *buffer, uint32_t total_sz); + }; + + /** + * The type used to represent DUIDs Based on Link-layer Address. + */ + struct duid_ll { + static const uint16_t duid_id = 3; + typedef std::vector lladdress_type; + + uint16_t hw_type; + lladdress_type lladdress; + + duid_ll(uint16_t hw_type = 0, + const lladdress_type &lladdress = lladdress_type()) + : hw_type(hw_type), lladdress(lladdress) {} + + PDU::serialization_type serialize() const; + + static duid_ll from_bytes(const uint8_t *buffer, uint32_t total_sz); + }; + + /** + * Type type used to represent DUIDs. This will be stored as the + * value for the Client/Server Identifier options. + */ + struct duid_type { + typedef PDU::serialization_type data_type; + + uint16_t id; + data_type data; + + duid_type(uint16_t id = 0, const data_type &data = data_type()) + : id(id), data(data) {} + + duid_type(const duid_llt &identifier) + : id(duid_llt::duid_id), data(identifier.serialize()) {} + + duid_type(const duid_en &identifier) + : id(duid_en::duid_id), data(identifier.serialize()) {} + + duid_type(const duid_ll &identifier) + : id(duid_en::duid_id), data(identifier.serialize()) {} + }; /** * The type used to store the Option Request option. @@ -599,6 +681,22 @@ public: */ bool has_reconfigure_accept() const; + /** + * \brief Getter for the Client Identifier option. + * + * This method will throw an option_not_found exception if the + * option is not found. + */ + duid_type client_id() const; + + /** + * \brief Getter for the Server Identifier option. + * + * This method will throw an option_not_found exception if the + * option is not found. + */ + duid_type server_id() const; + // Option setters /** @@ -718,6 +816,20 @@ public: */ void reconfigure_accept(); + /** + * \brief Setter for the Client Identifier option. + * + * \param value The new Client Identifier option data. + */ + void client_id(const duid_type &value); + + /** + * \brief Setter for the Server Identifier option. + * + * \param value The new Server Identifier option data. + */ + void server_id(const duid_type &value); + // Other stuff /** diff --git a/src/dhcpv6.cpp b/src/dhcpv6.cpp index 027730d..ad3d066 100644 --- a/src/dhcpv6.cpp +++ b/src/dhcpv6.cpp @@ -332,6 +332,32 @@ bool DHCPv6::has_reconfigure_accept() const { return search_option(RECONF_ACCEPT); } +DHCPv6::duid_type DHCPv6::client_id() const { + const dhcpv6_option *opt = safe_search_option( + CLIENTID, sizeof(uint16_t) + 1 + ); + return duid_type( + Endian::be_to_host(*(const uint16_t*)opt->data_ptr()), + serialization_type( + opt->data_ptr() + sizeof(uint16_t), + opt->data_ptr() + opt->data_size() + ) + ); +} + +DHCPv6::duid_type DHCPv6::server_id() const { + const dhcpv6_option *opt = safe_search_option( + SERVERID, sizeof(uint16_t) + 1 + ); + return duid_type( + Endian::be_to_host(*(const uint16_t*)opt->data_ptr()), + serialization_type( + opt->data_ptr() + sizeof(uint16_t), + opt->data_ptr() + opt->data_size() + ) + ); +} + // ******************************************************************** // Option setters // ******************************************************************** @@ -512,4 +538,109 @@ void DHCPv6::reconfigure_msg(uint8_t value) { void DHCPv6::reconfigure_accept() { add_option(RECONF_ACCEPT); } + + +// DUIDs + +DHCPv6::duid_llt DHCPv6::duid_llt::from_bytes(const uint8_t *buffer, uint32_t total_sz) +{ + // at least one byte for lladdress + if(total_sz < sizeof(uint16_t) + sizeof(uint32_t) + 1) + throw std::runtime_error("Not enough size for a DUID_LLT identifier"); + duid_llt output; + output.hw_type = Endian::be_to_host(*(const uint16_t*)buffer); + buffer += sizeof(uint16_t); + output.time = Endian::be_to_host(*(const uint32_t*)buffer); + buffer += sizeof(uint32_t); + total_sz -= sizeof(uint16_t) + sizeof(uint32_t); + output.lladdress.assign(buffer, buffer + total_sz); + return output; +} + +PDU::serialization_type DHCPv6::duid_llt::serialize() const { + serialization_type output(sizeof(uint16_t) + sizeof(uint32_t) + lladdress.size()); + *(uint16_t*)&output[0] = Endian::host_to_be(hw_type); + *(uint32_t*)&output[sizeof(uint16_t)] = Endian::host_to_be(time); + std::copy( + lladdress.begin(), + lladdress.end(), + output.begin() + sizeof(uint16_t) + sizeof(uint32_t) + ); + return output; +} + +DHCPv6::duid_en DHCPv6::duid_en::from_bytes(const uint8_t *buffer, uint32_t total_sz) +{ + // at least one byte for identifier + if(total_sz < sizeof(uint32_t) + 1) + throw std::runtime_error("Not enough size for a DUID_en identifier"); + duid_en output; + output.enterprise_number = Endian::be_to_host(*(const uint32_t*)buffer); + buffer += sizeof(uint32_t); + total_sz -= sizeof(uint32_t); + output.identifier.assign(buffer, buffer + total_sz); + return output; +} + +PDU::serialization_type DHCPv6::duid_en::serialize() const { + serialization_type output(sizeof(uint32_t) + identifier.size()); + *(uint32_t*)&output[0] = Endian::host_to_be(enterprise_number); + std::copy( + identifier.begin(), + identifier.end(), + output.begin() + sizeof(uint32_t) + ); + return output; +} + +DHCPv6::duid_ll DHCPv6::duid_ll::from_bytes(const uint8_t *buffer, uint32_t total_sz) +{ + // at least one byte for lladdress + if(total_sz < sizeof(uint16_t) + 1) + throw std::runtime_error("Not enough size for a DUID_en identifier"); + duid_ll output; + output.hw_type = Endian::be_to_host(*(const uint16_t*)buffer); + buffer += sizeof(uint16_t); + total_sz -= sizeof(uint16_t); + output.lladdress.assign(buffer, buffer + total_sz); + return output; +} + +PDU::serialization_type DHCPv6::duid_ll::serialize() const { + serialization_type output(sizeof(uint16_t) + lladdress.size()); + *(uint16_t*)&output[0] = Endian::host_to_be(hw_type); + std::copy( + lladdress.begin(), + lladdress.end(), + output.begin() + sizeof(uint16_t) + ); + return output; +} + +void DHCPv6::client_id(const duid_type &value) { + serialization_type buffer(sizeof(uint16_t) + value.data.size()); + *(uint16_t*)&buffer[0] = Endian::host_to_be(value.id); + std::copy( + value.data.begin(), + value.data.end(), + buffer.begin() + sizeof(uint16_t) + ); + add_option( + dhcpv6_option(CLIENTID, buffer.begin(), buffer.end()) + ); +} + +void DHCPv6::server_id(const duid_type &value) { + serialization_type buffer(sizeof(uint16_t) + value.data.size()); + *(uint16_t*)&buffer[0] = Endian::host_to_be(value.id); + std::copy( + value.data.begin(), + value.data.end(), + buffer.begin() + sizeof(uint16_t) + ); + add_option( + dhcpv6_option(SERVERID, buffer.begin(), buffer.end()) + ); +} + } // namespace Tins diff --git a/src/loopback.cpp b/src/loopback.cpp index 5be8c86..1ffa842 100644 --- a/src/loopback.cpp +++ b/src/loopback.cpp @@ -44,8 +44,8 @@ #include "llc.h" #include "rawpdu.h" -#if defined(BSD) && !defined(PF_LLC) - // compilation fix, check if it works xD +#if !defined(PF_LLC) + // compilation fix, nasty but at least works on BSD #define PF_LLC 26 #endif diff --git a/tests/src/dhcpv6.cpp b/tests/src/dhcpv6.cpp index db05161..6d5f23b 100644 --- a/tests/src/dhcpv6.cpp +++ b/tests/src/dhcpv6.cpp @@ -275,3 +275,65 @@ TEST_F(DHCPv6Test, ReconfigureAccept) { dhcp.reconfigure_accept(); EXPECT_EQ(true, dhcp.has_reconfigure_accept()); } + +TEST_F(DHCPv6Test, Client_Server_ID_DUIDLL) { + DHCPv6 dhcp; + DHCPv6::duid_ll data, output; + DHCPv6::duid_type tmp, tmp2; + data.hw_type = 0x5f; + data.lladdress.push_back(78); + data.lladdress.push_back(66); + data.lladdress.push_back(209); + dhcp.client_id(data); + tmp = dhcp.client_id(); + output = DHCPv6::duid_ll::from_bytes(&tmp.data[0], tmp.data.size()); + EXPECT_EQ(data.hw_type, output.hw_type); + EXPECT_EQ(data.lladdress, output.lladdress); + + dhcp.server_id(data); + tmp2 = dhcp.server_id(); + EXPECT_EQ(tmp.id, tmp2.id); + EXPECT_EQ(tmp.data, tmp2.data); +} + +TEST_F(DHCPv6Test, Client_Server_ID_DUIDLLT) { + DHCPv6 dhcp; + DHCPv6::duid_llt data, output; + DHCPv6::duid_type tmp, tmp2; + data.hw_type = 0x5f; + data.time = 0x92837af; + data.lladdress.push_back(78); + data.lladdress.push_back(66); + data.lladdress.push_back(209); + dhcp.client_id(data); + tmp = dhcp.client_id(); + output = DHCPv6::duid_llt::from_bytes(&tmp.data[0], tmp.data.size()); + EXPECT_EQ(data.hw_type, output.hw_type); + EXPECT_EQ(data.time, output.time); + EXPECT_EQ(data.lladdress, output.lladdress); + + dhcp.server_id(data); + tmp2 = dhcp.server_id(); + EXPECT_EQ(tmp.id, tmp2.id); + EXPECT_EQ(tmp.data, tmp2.data); +} + +TEST_F(DHCPv6Test, Client_Server_ID_DUIDEN) { + DHCPv6 dhcp; + DHCPv6::duid_en data, output; + DHCPv6::duid_type tmp, tmp2; + data.enterprise_number = 0x5faa23da; + data.identifier.push_back(78); + data.identifier.push_back(66); + data.identifier.push_back(209); + dhcp.client_id(data); + tmp = dhcp.client_id(); + output = DHCPv6::duid_en::from_bytes(&tmp.data[0], tmp.data.size()); + EXPECT_EQ(data.enterprise_number, output.enterprise_number); + EXPECT_EQ(data.identifier, output.identifier); + + dhcp.server_id(data); + tmp2 = dhcp.server_id(); + EXPECT_EQ(tmp.id, tmp2.id); + EXPECT_EQ(tmp.data, tmp2.data); +}