Commit 38ebe09e authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[2417] Create basic set of stdandard DHCPv6 option definition instances.

parent e8a6aee2
......@@ -26,6 +26,7 @@
#include <cc/data.h>
#include <config/ccsession.h>
#include <log/logger_support.h>
#include <dhcp/libdhcp++.h>
#include <dhcp/triplet.h>
#include <dhcp/pool.h>
#include <dhcp/subnet.h>
......@@ -601,7 +602,7 @@ private:
<< " be equal to zero. Option code '0' is reserved in"
<< " DHCPv6.");
} else if (option_code > std::numeric_limits<uint16_t>::max()) {
isc_throw(Dhcp6ConfigError, "Parser error: value of 'code' must not"
isc_throw(Dhcp6ConfigError, "Parser error: value of 'code' must not"ciwtezcowy
<< " exceed " << std::numeric_limits<uint16_t>::max());
}
// Check the option name has been specified, is non-empty and does not
......@@ -625,15 +626,37 @@ private:
<< " string of hexadecimal digits: " << option_data);
}
// Create the actual option.
// @todo Currently we simply create dhcp::Option instance here but we will
// need to use dedicated factory functions once the option definitions are
// created for all options.
OptionPtr option(new Option(Option::V6, static_cast<uint16_t>(option_code),
binary));
// If option is created succesfully, add it to the storage.
options_->push_back(option);
OptionDefContainer option_defs = LibDHCP::getOptionDefs(Option::V6);
const OptionDefContainerTypeIndex& idx = option_defs.get<1>();
const OptionDefContainerTypeRange& range = idx.equal_range(option_code);
size_t num_defs = std::distance(range.first, range.second);
OptionPtr option;
if (num_defs > 1) {
isc_throw(Dhcp6ConfigError, "Internal error: currently it is not"
<< " supported to initialize multiple option definitions"
<< " for the same option code. This will be supported once"
<< " there option spaces are implemented.");
} else if (num_defs == 0) {
// Create the actual option.
OptionPtr option(new Option(Option::V6, static_cast<uint16_t>(option_code),
binary));
// If option is created succesfully, add it to the storage.
options_->push_back(option);
} else {
const OptionDefinitionPtr& def = *(range.first);
// getFactory should never return NULL pointer so we skip
// sanity check here.
Option::Factory* factory = def->getFactory();
try {
OptionPtr option = factory(Option::V6, option_code, binary);
options_->push_back(option);
} catch (const isc::Exception& ex) {
isc_throw(Dhcp6ConfigError, "Parser error: option data does not match"
<< " option definition (code " << option_code << "): "
<< ex.what());
}
}
}
/// @brief Get a parameter from the strings storage.
......
......@@ -52,10 +52,17 @@ Dhcpv6Srv::Dhcpv6Srv(uint16_t port) {
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_OPEN_SOCKET).arg(port);
// First call to instance() will create IfaceMgr (it's a singleton)
// it may throw something if things go wrong
// Initialize objects required for DHCP server operation.
try {
// Initialize standard DHCPv6 option definitions. This function
// may throw bad_alloc if system goes out of memory during the
// creation if option definitions. It may also throw isc::Unexpected
// if definitions are wrong. This would mean error in implementation.
initStdOptionDefs();
// Call IfaceMgr::instance() will create instance of Interface
// Manager (it's a singleton). It may throw if things go wrong.
if (IfaceMgr::instance().countIfaces() == 0) {
LOG_ERROR(dhcp6_logger, DHCP6_NO_INTERFACES);
shutdown_ = true;
......@@ -432,6 +439,5 @@ Dhcpv6Srv::serverReceivedPacketName(uint8_t type) {
void
Dhcpv6Srv::initStdOptionDefs() {
OptionDefContainer options;
LibDHCP::initStdOptionDefs6(options);
LibDHCP::initStdOptionDefs(Option::V6);
}
......@@ -19,6 +19,7 @@
#include <dhcp/dhcp6.h>
#include <dhcp/pkt6.h>
#include <dhcp/option.h>
#include <dhcp/option_definition.h>
#include <iostream>
namespace isc {
......@@ -210,6 +211,7 @@ protected:
/// it is limited to critical options only.
void initStdOptionDefs();
private:
/// server DUID (to be sent in server-identifier option)
boost::shared_ptr<isc::dhcp::Option> serverid_;
......
......@@ -17,6 +17,8 @@
#include <fstream>
#include <sstream>
#include <boost/foreach.hpp>
#include <arpa/inet.h>
#include <gtest/gtest.h>
......@@ -25,6 +27,7 @@
#include <config/ccsession.h>
#include <dhcp/subnet.h>
#include <dhcp/cfgmgr.h>
#include <dhcp/option6_ia.h>
using namespace std;
using namespace isc;
......@@ -60,6 +63,24 @@ public:
/// param value.
std::string createConfigWithOption(const std::string& param_value,
const std::string& parameter) {
std::map<std::string, std::string> params;
if (parameter == "name") {
params["name"] = param_value;
params["code"] = "80";
params["data"] = "AB CDEF0105";
} else if (parameter == "code") {
params["name"] = "option_foo";
params["code"] = param_value;
params["data"] = "AB CDEF0105";
} else if (parameter == "data") {
params["name"] = "option_foo";
params["code"] = "80";
params["data"] = param_value;
}
return (createConfigWithOption(params));
}
std::string createConfigWithOption(const std::map<std::string, std::string>& params) {
std::ostringstream stream;
stream << "{ \"interface\": [ \"all\" ],"
"\"preferred-lifetime\": 3000,"
......@@ -69,21 +90,21 @@ public:
" \"pool\": [ \"2001:db8:1::/80\" ],"
" \"subnet\": \"2001:db8:1::/64\", "
" \"option-data\": [ {";
if (parameter == "name") {
stream <<
" \"name\": \"" << param_value << "\","
" \"code\": 80,"
" \"data\": \"AB CDEF0105\"";
} else if (parameter == "code") {
stream <<
" \"name\": \"option_foo\","
" \"code\": " << param_value << ","
" \"data\": \"AB CDEF0105\"";
} else if (parameter == "data") {
stream <<
" \"name\": \"option_foo\","
" \"code\": 80,"
" \"data\": \"" << param_value << "\"";
bool first = true;
typedef std::pair<std::string, std::string> ParamPair;
BOOST_FOREACH(ParamPair param, params) {
if (!first) {
stream << ", ";
} else {
first = false;
}
if (param.first == "name") {
stream << "\"name\": \"" << param.second << "\"";
} else if (param.first == "code") {
stream << "\"code\": " << param.second << "";
} else if (param.first == "data") {
stream << "\"data\": \"" << param.second << "\"";
}
}
stream <<
" } ]"
......@@ -658,4 +679,58 @@ TEST_F(Dhcp6ParserTest, optionDataLowerCase) {
testOption(*range.first, 80, foo_expected, sizeof(foo_expected));
}
// Verify that specific option object is returned for standard
// option which has dedicated option class derived from Option.
TEST_F(Dhcp6ParserTest, stdOptionData) {
ConstElementPtr x;
std::map<std::string, std::string> params;
params["name"] = "OPTION_IA_NA";
// Option code 3 means OPTION_IA_NA.
params["code"] = "3";
params["data"] = "ABCDEF01 02030405 06070809";
std::string config = createConfigWithOption(params);
ElementPtr json = Element::fromJSON(config);
EXPECT_NO_THROW(x = configureDhcp6Server(*srv_, json));
ASSERT_TRUE(x);
comment_ = parseAnswer(rcode_, x);
ASSERT_EQ(0, rcode_);
Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"));
ASSERT_TRUE(subnet);
const Subnet::OptionContainer& options = subnet->getOptions();
ASSERT_EQ(1, options.size());
// Get the search index. Index #1 is to search using option code.
const Subnet::OptionContainerTypeIndex& idx = options.get<1>();
// Get the options for specified index. Expecting one option to be
// returned but in theory we may have multiple options with the same
// code so we get the range.
std::pair<Subnet::OptionContainerTypeIndex::const_iterator,
Subnet::OptionContainerTypeIndex::const_iterator> range =
idx.equal_range(D6O_IA_NA);
// Expect single option with the code equal to IA_NA option code.
ASSERT_EQ(1, std::distance(range.first, range.second));
// The actual pointer to the option is held in the option field
// in the structure returned.
OptionPtr option = range.first->option;
ASSERT_TRUE(option);
// Option object returned for here is expected to be Option6IA
// which is derived from Option. This class is dedicated to
// represent standard option IA_NA.
boost::shared_ptr<Option6IA> optionIA =
boost::dynamic_pointer_cast<Option6IA>(option);
// If cast is unsuccessful than option returned was of a
// differnt type than Option6IA. This is wrong.
ASSERT_TRUE(optionIA);
// If cast was successful we may use accessors exposed by
// Option6IA to validate that the content of this option
// has been set correctly.
EXPECT_EQ(0xABCDEF01, optionIA->getIAID());
EXPECT_EQ(0x02030405, optionIA->getT1());
EXPECT_EQ(0x06070809, optionIA->getT2());
}
};
......@@ -35,6 +35,23 @@ std::map<unsigned short, Option::Factory*> LibDHCP::v4factories_;
// static array with factories for options
std::map<unsigned short, Option::Factory*> LibDHCP::v6factories_;
// Static container with DHCPv4 option definitions.
OptionDefContainer LibDHCP::v4option_defs_;
// Static container with DHCPv6 option definitions.
OptionDefContainer LibDHCP::v6option_defs_;
const OptionDefContainer&
LibDHCP::getOptionDefs(Option::Universe u) {
switch (u) {
case Option::V4:
return (v4option_defs_);
case Option::V6:
return (v6option_defs_);
default:
isc_throw(isc::BadValue, "invalid universe " << u << " specified");
}
}
OptionPtr
LibDHCP::optionFactory(Option::Universe u,
......@@ -204,8 +221,27 @@ void LibDHCP::OptionFactoryRegister(Option::Universe u,
}
void
LibDHCP::initStdOptionDefs6(OptionDefContainer& defs) {
defs.clear();
LibDHCP::initStdOptionDefs(Option::Universe u) {
switch (u) {
case Option::V4:
initStdOptionDefs4();
break;
case Option::V6:
initStdOptionDefs6();
break;
default:
isc_throw(isc::BadValue, "invalid universe " << u << " specified");
}
}
void
LibDHCP::initStdOptionDefs4() {
isc_throw(isc::NotImplemented, "initStdOptionDefs4 is not implemented");
}
void
LibDHCP::initStdOptionDefs6() {
v6option_defs_.clear();
struct OptionParams {
std::string name;
......@@ -243,11 +279,18 @@ LibDHCP::initStdOptionDefs6(OptionDefContainer& defs) {
definition->addRecordField(OptionDefinition::UINT32_TYPE);
break;
case D6O_STATUS_CODE:
definition->addRecordField(OptionDefinition::UINT16_TYPE);
definotion->addRecordField(OptionDefinition::UINT16_TYPE);
definition->addRecordField(OptionDefinition::STRING_TYPE);
default:
break;
}
defs.push_back(definition);
try {
definition->validate();
} catch (const Exception& ex) {
isc_throw(isc::Unexpected, "internal server error: invalid definition of standard"
<< " DHCPv6 option (with code " << params[i].code << "): "
<< ex.what());
}
v6option_defs_.push_back(definition);
}
}
......@@ -30,6 +30,12 @@ public:
/// Map of factory functions.
typedef std::map<unsigned short, Option::Factory*> FactoryMap;
/// @brief Return collection of option definitions.
///
/// @param u universe of the options (V4 or V6).
/// @return collection of option definitions.
static const OptionDefContainer& getOptionDefs(Option::Universe u);
/// @brief Factory function to create instance of option.
///
/// Factory method creates instance of specified option. The option
......@@ -104,14 +110,53 @@ public:
uint16_t type,
Option::Factory * factory);
static void initStdOptionDefs6(OptionDefContainer& defs);
/// Initialize standard DHCP options (V4 or V6).
///
/// The method creates option definitions for all options
/// (DHCPv4 or DHCPv6 depending on universe specified).
/// Currently DHCPv4 option definitions initialization is not
/// implemented thus this function will throw isc::NotImplemented
/// if V4 universe is specified.
///
/// @param u universe
/// @throw isc::Unexpected if internal error occured during option
/// definitions creation.
/// @throw std::bad_alloc if system went out of memory.
/// @throw isc::NotImplemented when V4 universe specified.
static void initStdOptionDefs(Option::Universe u);
private:
/// Initialize standard DHCPv4 option definitions.
///
/// The method creates option definitions for all DHCPv4 options.
/// Currently this function is not implemented.
///
/// @todo implemend this function.
///
/// @throw isc::NotImplemeneted
static void initStdOptionDefs4();
/// Initialize standard DHCPv6 option definitions.
///
/// The method creates option definitions for all DHCPv6 options.
///
/// @throw isc::Unexpected if internal error occured during option
/// definitions creation.
/// @throw std::bad_alloc if system went out of memory.
static void initStdOptionDefs6();
protected:
/// pointers to factories that produce DHCPv6 options
static FactoryMap v4factories_;
/// pointers to factories that produce DHCPv6 options
static FactoryMap v6factories_;
/// Container with DHCPv4 option definitions.
static OptionDefContainer v4option_defs_;
/// Container with DHCPv6 option definitions.
static OptionDefContainer v6option_defs_;
};
}
......
......@@ -64,8 +64,8 @@ public:
static void testInitOptionDefs6(const uint16_t code,
const OptionBuffer& buf,
const std::type_info& expected_type) {
OptionDefContainer options;
LibDHCP::initStdOptionDefs6(options);
LibDHCP::initStdOptionDefs(Option::V6);
OptionDefContainer options = LibDHCP::getOptionDefs(Option::V6);
const OptionDefContainerTypeIndex& idx = options.get<1>();
OptionDefContainerTypeRange range = idx.equal_range(code);
ASSERT_EQ(1, std::distance(range.first, range.second));
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment