#include <dhcp6/dhcp6_srv.h>
#include <dhcp/dhcp6.h>
#include <dhcp/iface_mgr.h>
+#include <dhcp/libdhcp++.h>
#include <dhcp/option6_addrlst.h>
#include <dhcp/option6_iaaddr.h>
#include <dhcp/option6_ia.h>
}
return (UNKNOWN);
}
+
+void
+Dhcpv6Srv::initStdOptionDefs() {
+ OptionDefContainer options;
+ LibDHCP::initStdOptionDefs6(options);
+}
/// 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<isc::dhcp::Option> serverid_;
#include <dhcp/option.h>
#include <dhcp/option6_ia.h>
#include <dhcp/option6_iaaddr.h>
+#include <dhcp/option_definition.h>
using namespace std;
using namespace isc::dhcp;
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);
+ }
+}
-// 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
#ifndef LIBDHCP_H_
#define LIBDHCP_H_
-#include <iostream>
-#include <util/buffer.h>
+#include <dhcp/option_definition.h>
#include <dhcp/pkt6.h>
+#include <util/buffer.h>
+#include <iostream>
namespace isc {
namespace dhcp {
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_;
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;
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);
#define OPTION_DEFINITION_H_
#include <dhcp/option_data_types.h>
-#include <dhcp/option6_int.h>
-#include <dhcp/option6_int_array.h>
#include <dhcp/option.h>
+#include <boost/shared_ptr.hpp>
+#include <boost/multi_index_container.hpp>
+#include <boost/multi_index/hashed_index.hpp>
+#include <boost/multi_index/sequenced_index.hpp>
+#include <boost/multi_index/mem_fun.hpp>
namespace isc {
namespace dhcp {
+/// @brief Forward declaration to OptionDefinition.
+class OptionDefinition;
+
+/// @brief Pointer to option definition object.
+typedef boost::shared_ptr<OptionDefinition> 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<typename T>
+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<typename T>
+class Option6IntArray;
+
/// @brief Base class representing a DHCP option definition.
///
/// This is a base class representing a DHCP option definition, which describes
///
/// 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
/// Data types of DHCP option fields.
enum DataType {
EMPTY_TYPE,
+ BINARY_TYPE,
BOOLEAN_TYPE,
INT8_TYPE,
INT16_TYPE,
/// @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.
};
+/// @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<OptionDefContainerTypeIndex::const_iterator,
+ OptionDefContainerTypeIndex::const_iterator> OptionDefContainerTypeRange;
+
+
} // namespace isc::dhcp
} // namespace isc
#include <dhcp/dhcp4.h>
#include <dhcp/dhcp6.h>
#include <dhcp/libdhcp++.h>
+#include <dhcp/option6_ia.h>
+#include <dhcp/option6_iaaddr.h>
+#include <dhcp/option6_int.h>
+#include <dhcp/option6_int_array.h>
+#include <dhcp/option6_addrlst.h>
#include "config.h"
using namespace std;
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[] = {
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<uint16_t>));
+ LibDhcpTest::testInitOptionDefs6(D6O_ELAPSED_TIME, OptionBuffer(2, 1),
+ typeid(Option6Int<uint16_t>));
+ 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));
+}
+
}
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;