From: Marcin Siodelski Date: Wed, 31 Oct 2012 20:16:21 +0000 (+0100) Subject: [2417] Creating definitions for critical standard DHCPv6 options. X-Git-Tag: trac2487_base~18^2^2~6 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=e8a6aee2464c295e476eff03448e39d386747140;p=thirdparty%2Fkea.git [2417] Creating definitions for critical standard DHCPv6 options. --- diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc index 7c57a6139c..330873aac2 100644 --- a/src/bin/dhcp6/dhcp6_srv.cc +++ b/src/bin/dhcp6/dhcp6_srv.cc @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -428,3 +429,9 @@ Dhcpv6Srv::serverReceivedPacketName(uint8_t type) { } return (UNKNOWN); } + +void +Dhcpv6Srv::initStdOptionDefs() { + OptionDefContainer options; + LibDHCP::initStdOptionDefs6(options); +} diff --git a/src/bin/dhcp6/dhcp6_srv.h b/src/bin/dhcp6/dhcp6_srv.h index 1cb323657a..2a916bedc5 100644 --- a/src/bin/dhcp6/dhcp6_srv.h +++ b/src/bin/dhcp6/dhcp6_srv.h @@ -199,6 +199,17 @@ protected: /// interfaces for new DUID generation are detected. void setServerID(); + /// @brief Initializes option definitions for standard options. + /// + /// Each standard option's format is described by the + /// dhcp::OptionDefinition object. This function creates such objects + /// for each standard DHCPv6 option. + /// + /// @todo list thrown exceptions. + /// @todo extend this function to cover all standard options. Currently + /// it is limited to critical options only. + void initStdOptionDefs(); + /// server DUID (to be sent in server-identifier option) boost::shared_ptr serverid_; diff --git a/src/lib/dhcp/libdhcp++.cc b/src/lib/dhcp/libdhcp++.cc index 12e1bbdfd4..f182862889 100644 --- a/src/lib/dhcp/libdhcp++.cc +++ b/src/lib/dhcp/libdhcp++.cc @@ -23,6 +23,7 @@ #include #include #include +#include using namespace std; using namespace isc::dhcp; @@ -201,3 +202,52 @@ void LibDHCP::OptionFactoryRegister(Option::Universe u, return; } + +void +LibDHCP::initStdOptionDefs6(OptionDefContainer& defs) { + defs.clear(); + + struct OptionParams { + std::string name; + uint16_t code; + OptionDefinition::DataType type; + bool array; + }; + OptionParams params[] = { + { "CLIENTID", D6O_CLIENTID, OptionDefinition::BINARY_TYPE, false }, + { "SERVERID", D6O_SERVERID, OptionDefinition::BINARY_TYPE, false }, + { "IA_NA", D6O_IA_NA, OptionDefinition::RECORD_TYPE, false }, + { "IAADDR", D6O_IAADDR, OptionDefinition::RECORD_TYPE, false }, + { "ORO", D6O_ORO, OptionDefinition::UINT16_TYPE, true }, + { "ELAPSED_TIME", D6O_ELAPSED_TIME, OptionDefinition::UINT16_TYPE, false }, + { "STATUS_CODE", D6O_STATUS_CODE, OptionDefinition::RECORD_TYPE, false }, + { "RAPID_COMMIT", D6O_RAPID_COMMIT, OptionDefinition::EMPTY_TYPE, false }, + { "DNS_SERVERS", D6O_NAME_SERVERS, OptionDefinition::IPV6_ADDRESS_TYPE, true } + }; + const int params_size = sizeof(params) / sizeof(params[0]); + + for (int i = 0; i < params_size; ++i) { + OptionDefinitionPtr definition(new OptionDefinition(params[i].name, + params[i].code, + params[i].type, + params[i].array)); + switch(params[i].code) { + case D6O_IA_NA: + for (int j = 0; j < 3; ++j) { + definition->addRecordField(OptionDefinition::UINT32_TYPE); + } + break; + case D6O_IAADDR: + definition->addRecordField(OptionDefinition::IPV6_ADDRESS_TYPE); + definition->addRecordField(OptionDefinition::UINT32_TYPE); + definition->addRecordField(OptionDefinition::UINT32_TYPE); + break; + case D6O_STATUS_CODE: + definition->addRecordField(OptionDefinition::UINT16_TYPE); + definition->addRecordField(OptionDefinition::STRING_TYPE); + default: + break; + } + defs.push_back(definition); + } +} diff --git a/src/lib/dhcp/libdhcp++.h b/src/lib/dhcp/libdhcp++.h index ae907012cc..e565a35635 100644 --- a/src/lib/dhcp/libdhcp++.h +++ b/src/lib/dhcp/libdhcp++.h @@ -1,4 +1,4 @@ -// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2011-2012 Internet Systems Consortium, Inc. ("ISC") // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above @@ -15,9 +15,10 @@ #ifndef LIBDHCP_H_ #define LIBDHCP_H_ -#include -#include +#include #include +#include +#include namespace isc { namespace dhcp { @@ -102,6 +103,9 @@ public: static void OptionFactoryRegister(Option::Universe u, uint16_t type, Option::Factory * factory); + + static void initStdOptionDefs6(OptionDefContainer& defs); + protected: /// pointers to factories that produce DHCPv6 options static FactoryMap v4factories_; diff --git a/src/lib/dhcp/option_definition.cc b/src/lib/dhcp/option_definition.cc index f562316a0a..87b3216306 100644 --- a/src/lib/dhcp/option_definition.cc +++ b/src/lib/dhcp/option_definition.cc @@ -29,6 +29,7 @@ namespace dhcp { OptionDefinition::DataTypeUtil::DataTypeUtil() { data_types_["empty"] = EMPTY_TYPE; + data_types_["binary"] = BINARY_TYPE; data_types_["boolean"] = BOOLEAN_TYPE; data_types_["int8"] = INT8_TYPE; data_types_["int16"] = INT16_TYPE; @@ -97,11 +98,15 @@ OptionDefinition::addRecordField(const DataType data_type) { Option::Factory* OptionDefinition::getFactory() const { + validate(); + // @todo This function must be extended to return more factory // functions that create instances of more specialized options. // This requires us to first implement those more specialized // options that will be derived from Option class. - if (type_ == IPV6_ADDRESS_TYPE && array_type_) { + if (type_ == BINARY_TYPE) { + return (factoryGeneric); + } else if (type_ == IPV6_ADDRESS_TYPE && array_type_) { return (factoryAddrList6); } else if (type_ == IPV4_ADDRESS_TYPE && array_type_) { return (factoryAddrList4); diff --git a/src/lib/dhcp/option_definition.h b/src/lib/dhcp/option_definition.h index c274ce9275..27b2bdaab4 100644 --- a/src/lib/dhcp/option_definition.h +++ b/src/lib/dhcp/option_definition.h @@ -16,13 +16,42 @@ #define OPTION_DEFINITION_H_ #include -#include -#include #include +#include +#include +#include +#include +#include namespace isc { namespace dhcp { +/// @brief Forward declaration to OptionDefinition. +class OptionDefinition; + +/// @brief Pointer to option definition object. +typedef boost::shared_ptr OptionDefinitionPtr; + +/// @brief Forward declaration to Option6Int. +/// +/// This forward declaration is needed to access Option6Int class +/// without having to include option6_int.h header. This is because +/// this header includes libdhcp++.h and this causes circular +/// inclusion between libdhcp++.h, option_definition.h and +/// option6_int.h. +template +class Option6Int; + +/// @brief Forward declaration to Option6IntArray. +/// +/// This forward declaration is needed to access Option6IntArray class +/// without having to include option6_int_array.h header. This is because +/// this header includes libdhcp++.h and this causes circular +/// inclusion between libdhcp++.h, option_definition.h and +/// option6_int_array.h. +template +class Option6IntArray; + /// @brief Base class representing a DHCP option definition. /// /// This is a base class representing a DHCP option definition, which describes @@ -52,7 +81,7 @@ namespace dhcp { /// /// Should the option comprise data fields of different types, the "record" /// option type is used. In such cases the data field types within the record -/// are specified using \ref OptionDefinition::addRecordField. +/// are specified using \ref OptioDefinition::addRecordField. /// /// When the OptionDefinition object has been sucessfully created, it can be /// queried to return the appropriate option factory function for the specified @@ -84,6 +113,7 @@ public: /// Data types of DHCP option fields. enum DataType { EMPTY_TYPE, + BINARY_TYPE, BOOLEAN_TYPE, INT8_TYPE, INT16_TYPE, @@ -202,7 +232,9 @@ public: /// @brief Return factory function for the given definition. /// - /// @return pointer to factory function. + /// @throw isc::OutOfRange if \ref validate returns it. + /// @throw isc::BadValue if \ref validate returns it. + /// @return pointer to a factory function. Option::Factory* getFactory() const; /// @brief Return option name. @@ -377,6 +409,50 @@ private: }; +/// @brief Multi index container for DHCP option definitions. +/// +/// This container allows to search for DHCP option definition +/// using two indexes: +/// - sequenced: used to access elements in the oreder they have +/// been added to the container +/// - option code: used to search defintions of options +/// with a specified option code (aka option type). +/// +/// @todo: need an index to search options using option space name +/// once option spaces are implemented. +typedef boost::multi_index_container< + // Container comprises elements of OptionDefinition type. + OptionDefinitionPtr, + // Here we start enumerating various indexes. + boost::multi_index::indexed_by< + // Sequenced index allows accessing elements in the same way + // as elements in std::list. Sequenced is an index #0. + boost::multi_index::sequenced<>, + // Start definition of index #1. + boost::multi_index::hashed_non_unique< + // Use option type as the index key. The type is held + // in OptionDefinition object so we have to call + // OptionDefinition::getCode to retrieve this key + // for each element. + boost::multi_index::const_mem_fun< + OptionDefinition, + uint16_t, + &OptionDefinition::getCode + > + > + > +> OptionDefContainer; + +/// Type of the index #1 - option type. +typedef OptionDefContainer::nth_index<1>::type OptionDefContainerTypeIndex; +/// Pair of iterators to represent the range of options definitions +/// having the same option type value. The first element in this pair +/// represents the begining of the range, the second element +/// represents the end. +typedef std::pair OptionDefContainerTypeRange; + + } // namespace isc::dhcp } // namespace isc diff --git a/src/lib/dhcp/tests/libdhcp++_unittest.cc b/src/lib/dhcp/tests/libdhcp++_unittest.cc index d17eb6537f..650a0f39a8 100644 --- a/src/lib/dhcp/tests/libdhcp++_unittest.cc +++ b/src/lib/dhcp/tests/libdhcp++_unittest.cc @@ -21,6 +21,11 @@ #include #include #include +#include +#include +#include +#include +#include #include "config.h" using namespace std; @@ -46,6 +51,34 @@ public: Option* option = new Option(u, type, buf); return OptionPtr(option); } + + /// @brief Test option option definition. + /// + /// This function tests if option definition for standard + /// option has been initialized correctly. + /// + /// @param code option code. + /// @param bug buffer to be used to create option instance. + /// @param expected_type type of the option created by the + /// factory function returned by the option definition. + static void testInitOptionDefs6(const uint16_t code, + const OptionBuffer& buf, + const std::type_info& expected_type) { + OptionDefContainer options; + LibDHCP::initStdOptionDefs6(options); + const OptionDefContainerTypeIndex& idx = options.get<1>(); + OptionDefContainerTypeRange range = idx.equal_range(code); + ASSERT_EQ(1, std::distance(range.first, range.second)); + OptionDefinitionPtr def = *(range.first); + ASSERT_TRUE(def); + ASSERT_NO_THROW(def->validate()); + Option::Factory* factory = NULL; + ASSERT_NO_THROW(factory = def->getFactory()); + OptionPtr option; + ASSERT_NO_THROW(option = factory(Option::V6, code, buf)); + ASSERT_TRUE(option); + EXPECT_TRUE(typeid(*option) == expected_type); + } }; static const uint8_t packed[] = { @@ -311,4 +344,30 @@ TEST(LibDhcpTest, unpackOptions4) { EXPECT_TRUE(x == options.end()); // option 2 not found } +// Test that definitions of standard options have been initialized +// correctly. +// @todo Only limited number of option definitions are now created +// This test have to be extended once all option definitions are +// created. +TEST(LibDhcpTest, initStdOptionDefs) { + LibDhcpTest::testInitOptionDefs6(D6O_CLIENTID, OptionBuffer(14, 1), + typeid(Option)); + LibDhcpTest::testInitOptionDefs6(D6O_SERVERID, OptionBuffer(14, 1), + typeid(Option)); + LibDhcpTest::testInitOptionDefs6(D6O_IA_NA, OptionBuffer(12, 1), + typeid(Option6IA)); + LibDhcpTest::testInitOptionDefs6(D6O_IAADDR, OptionBuffer(24, 1), + typeid(Option6IAAddr)); + LibDhcpTest::testInitOptionDefs6(D6O_ORO, OptionBuffer(10, 1), + typeid(Option6IntArray)); + LibDhcpTest::testInitOptionDefs6(D6O_ELAPSED_TIME, OptionBuffer(2, 1), + typeid(Option6Int)); + LibDhcpTest::testInitOptionDefs6(D6O_STATUS_CODE, OptionBuffer(10, 1), + typeid(Option)); + LibDhcpTest::testInitOptionDefs6(D6O_RAPID_COMMIT, OptionBuffer(), + typeid(Option)); + LibDhcpTest::testInitOptionDefs6(D6O_NAME_SERVERS, OptionBuffer(32, 1), + typeid(Option6AddrLst)); +} + } diff --git a/src/lib/dhcp/tests/option_definition_unittest.cc b/src/lib/dhcp/tests/option_definition_unittest.cc index 4b83517ee6..99e8a5e526 100644 --- a/src/lib/dhcp/tests/option_definition_unittest.cc +++ b/src/lib/dhcp/tests/option_definition_unittest.cc @@ -281,6 +281,43 @@ TEST_F(OptionDefinitionTest, factoryEmpty) { EXPECT_THROW(factory(Option::V6, D6O_RAPID_COMMIT,OptionBuffer(2)),isc::BadValue); } +TEST_F(OptionDefinitionTest, factoryBinary) { + OptionDefinition opt_def("OPTION_SERVERID", D6O_SERVERID, "binary"); + Option::Factory* factory(NULL); + EXPECT_NO_THROW(factory = opt_def.getFactory()); + ASSERT_TRUE(factory != NULL); + + OptionBuffer buf(14); + for (int i = 0; i < 14; ++i) { + buf[i] = i; + } + OptionPtr option_v6; + ASSERT_NO_THROW( + option_v6 = factory(Option::V6, D6O_SERVERID, buf); + ); + // Expect base option type returned. + ASSERT_TRUE(typeid(*option_v6) == typeid(Option)); + EXPECT_EQ(Option::V6, option_v6->getUniverse()); + EXPECT_EQ(4, option_v6->getHeaderLen()); + ASSERT_EQ(buf.size(), option_v6->getData().size()); + + EXPECT_TRUE(std::equal(option_v6->getData().begin(), + option_v6->getData().end(), + buf.begin())); + + // Repeat the same test scenario for DHCPv4 option. + OptionPtr option_v4; + ASSERT_NO_THROW(option_v4 = factory(Option::V4, 214, buf)); + // Expect 'empty' DHCPv4 option. + EXPECT_EQ(Option::V4, option_v4->getUniverse()); + EXPECT_EQ(2, option_v4->getHeaderLen()); + ASSERT_EQ(buf.size(), option_v4->getData().size()); + + EXPECT_TRUE(std::equal(option_v6->getData().begin(), + option_v6->getData().end(), + buf.begin())); +} + TEST_F(OptionDefinitionTest, factoryIA6) { // This option consists of IAID, T1 and T2 fields (each 4 bytes long). const int option6_ia_len = 12;