Commit 2548205b authored by Marcin Siodelski's avatar Marcin Siodelski

[2315] Changes as the result of the review.

parent bf592d06
......@@ -74,6 +74,34 @@ LibDHCP::getOptionDef(const Option::Universe u, const uint16_t code) {
return (OptionDefinitionPtr());
}
bool
LibDHCP::isStandardOption(const Option::Universe u, const uint16_t code) {
if (u == Option::V6) {
if (code < 79 &&
code != 10 &&
code != 35) {
return (true);
}
} else if (u == Option::V4) {
if (!(code == 84 ||
code == 96 ||
(code > 101 && code < 112) ||
code == 115 ||
code == 126 ||
code == 127 ||
(code > 146 && code < 150) ||
(code > 177 && code < 208) ||
(code > 213 && code < 220) ||
(code > 221 && code < 224))) {
return (true);
}
}
return (false);
}
OptionPtr
LibDHCP::optionFactory(Option::Universe u,
uint16_t type,
......
......@@ -55,6 +55,21 @@ public:
static OptionDefinitionPtr getOptionDef(const Option::Universe u,
const uint16_t code);
/// @brief Check if the specified option is a standard option.
///
/// @param u universe (V4 or V6)
/// @param code option code.
///
/// @return true if the specified option is a standard option.
/// @todo We arleady create option definitions for the subset if
/// standard options. We are aiming that this function checks
/// the presence of the standard option definition and if it finds
/// it, then the true value is returned. However, at this point
/// this is not doable because some of the definitions (for less
/// important options) are not created yet.
static bool isStandardOption(const Option::Universe u,
const uint16_t code);
/// @brief Factory function to create instance of option.
///
/// Factory method creates instance of specified option. The option
......
......@@ -500,6 +500,9 @@ typedef boost::multi_index_container<
>
> OptionDefContainer;
/// Pointer to an option definition container.
typedef boost::shared_ptr<OptionDefContainer> OptionDefContainerPtr;
/// Type of the index #1 - option type.
typedef OptionDefContainer::nth_index<1>::type OptionDefContainerTypeIndex;
/// Pair of iterators to represent the range of options definitions
......
......@@ -453,6 +453,66 @@ TEST_F(LibDhcpTest, unpackOptions4) {
EXPECT_TRUE(x == options.end()); // option 2 not found
}
TEST_F(LibDhcpTest, isStandardOption4) {
// Get all option codes that are not occupied by standard options.
const uint16_t unassigned_codes[] = { 84, 96, 102, 103, 104, 105, 106, 107, 108,
109, 110, 111, 115, 126, 127, 147, 148, 149,
178, 179, 180, 181, 182, 183, 184, 185, 186,
187, 188, 189, 190, 191, 192, 193, 194, 195,
196, 197, 198, 199, 200, 201, 202, 203, 204,
205, 206, 207, 214, 215, 216, 217, 218, 219,
222, 223 };
const size_t unassigned_num = sizeof(unassigned_codes) / sizeof(unassigned_codes[0]);
// Try all possible option codes.
for (size_t i = 0; i < 256; ++i) {
// Some ranges of option codes are unassigned and thus the isStandardOption
// should return false for them.
bool check_unassigned = false;
// Check the array of unassigned options to find out whether option code
// is assigned to standard option or unassigned.
for (size_t j = 0; j < unassigned_num; ++j) {
// If option code is found within the array of unassigned options
// we the isStandardOption function should return false.
if (unassigned_codes[j] == i) {
check_unassigned = true;
EXPECT_FALSE(LibDHCP::isStandardOption(Option::V4,
unassigned_codes[j]))
<< "Test failed for option code " << unassigned_codes[j];
break;
}
}
// If the option code belongs to the standard option then the
// isStandardOption should return true.
if (!check_unassigned) {
EXPECT_TRUE(LibDHCP::isStandardOption(Option::V4, i))
<< "Test failed for the option code " << i;
}
}
}
TEST_F(LibDhcpTest, isStandardOption6) {
// All option codes in the range from 0 to 78 (except 10 and 35)
// identify the standard options.
for (uint16_t code = 0; code < 79; ++code) {
if (code != 10 && code != 35) {
EXPECT_TRUE(LibDHCP::isStandardOption(Option::V6, code))
<< "Test failed for option code " << code;
}
}
// Check the option codes 10 and 35. They are unassigned.
EXPECT_FALSE(LibDHCP::isStandardOption(Option::V6, 10));
EXPECT_FALSE(LibDHCP::isStandardOption(Option::V6, 35));
// Check a range of option codes above 78. Those are option codes
// identifying non-standard options.
for (uint16_t code = 79; code < 512; ++code) {
EXPECT_FALSE(LibDHCP::isStandardOption(Option::V6, code))
<< "Test failed for option code " << code;
}
}
TEST_F(LibDhcpTest, stdOptionDefs4) {
// Create a buffer that holds dummy option data.
......
......@@ -13,6 +13,7 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <asiolink/io_address.h>
#include <dhcp/libdhcp++.h>
#include <dhcpsrv/cfgmgr.h>
using namespace isc::asiolink;
......@@ -34,23 +35,36 @@ CfgMgr::addOptionDef(const OptionDefinitionPtr& def,
// This will be implemented when #2313 is merged.
if (option_space.empty()) {
isc_throw(BadValue, "option space name must not be empty");
} else if (option_space == "dhcp4" || option_space == "dhcp6") {
isc_throw(BadValue, "unable to override definition of option"
<< " in standard option space '" << option_space
<< "'.");
} else if (!def) {
// Option definition must point to a valid object.
isc_throw(MalformedOptionDefinition, "option definition must not be NULL");
} else if (getOptionDef(option_space, def->getCode())) {
// Option definition must not be overriden.
isc_throw(DuplicateOptionDefinition, "option definition already added"
<< " to option space " << option_space);
} else if ((option_space == "dhcp4" &&
LibDHCP::isStandardOption(Option::V4, def->getCode())) ||
(option_space == "dhcp6" &&
LibDHCP::isStandardOption(Option::V6, def->getCode()))) {
// We must not override standard (assigned) option. The standard options
// belong to dhcp4 or dhcp6 option space.
isc_throw(BadValue, "unable to override definition of option '"
<< def->getCode() << "' in standard option space '"
<< option_space << "'.");
}
// Actually add the definition to the option space.
option_def_spaces_[option_space].push_back(def);
}
const OptionDefContainer&
CfgMgr::getOptionDefs(const std::string& option_space) const {
// @todo Validate the option space once the #2313 is implemented.
// Get all option definitions for the particular option space.
const std::map<std::string, OptionDefContainer>::const_iterator& defs =
const OptionDefsMap::const_iterator& defs =
option_def_spaces_.find(option_space);
// If there are no option definitions for the particular option space
// then return empty container.
......@@ -65,6 +79,8 @@ CfgMgr::getOptionDefs(const std::string& option_space) const {
OptionDefinitionPtr
CfgMgr::getOptionDef(const std::string& option_space,
const uint16_t option_code) const {
// @todo Validate the option space once the #2313 is implemented.
// Get a reference to option definitions for a particular option space.
const OptionDefContainer& defs = getOptionDefs(option_space);
// If there are no matching option definitions then return the empty pointer.
......
......@@ -88,7 +88,8 @@ public:
/// @throw isc::dhcp::MalformedOptionDefinition when the pointer to
/// an option definition is NULL.
/// @throw isc::BadValue when the option space name is empty or
/// option space name is one of reserved names: dhcp4 or dhcp6.
/// when trying to override the standard option (in dhcp4 or dhcp6
/// option space).
void addOptionDef(const OptionDefinitionPtr& def,
const std::string& option_space);
......@@ -186,6 +187,7 @@ public:
/// 192.0.2.0/23 and 192.0.2.0/24 the same subnet or is it something
/// completely new?
void deleteSubnets4();
protected:
/// @brief Protected constructor.
......@@ -217,9 +219,16 @@ protected:
private:
/// A map containing option definitions for various option spaces.
/// They key of this map is the name of the option space. The
/// value is the the option container holding option definitions
/// for the particular option space.
typedef std::map<std::string, OptionDefContainer> OptionDefsMap;
/// A map containing option definitions for different option spaces.
/// The map key holds an option space name.
std::map<std::string, OptionDefContainer> option_def_spaces_;
OptionDefsMap option_def_spaces_;
};
} // namespace isc::dhcp
......
......@@ -82,8 +82,8 @@ Subnet::getOptions(const std::string& option_space) const {
}
Subnet::OptionDescriptor
Subnet::getOptionSingle(const std::string& option_space,
const uint16_t option_code) {
Subnet::getOptionDescriptor(const std::string& option_space,
const uint16_t option_code) {
const OptionContainer& options = getOptions(option_space);
if (options.empty()) {
return (OptionDescriptor(false));
......
......@@ -270,8 +270,8 @@ public:
///
/// @return option descriptor found for the specified option space
/// and option code.
OptionDescriptor getOptionSingle(const std::string& option_space,
const uint16_t option_code);
OptionDescriptor getOptionDescriptor(const std::string& option_space,
const uint16_t option_code);
/// @brief returns the last address that was tried from this pool
///
......
......@@ -129,7 +129,7 @@ TEST_F(CfgMgrTest, getOptionDef) {
// add them to the different option space.
for (uint16_t code = 105; code < 115; ++code) {
std::ostringstream option_name;
option_name << "option-" << code;
option_name << "option-other-" << code;
OptionDefinitionPtr def(new OptionDefinition(option_name.str(), code,
"uint16"));
ASSERT_NO_THROW(cfg_mgr.addOptionDef(def, "abcde"));
......@@ -140,6 +140,12 @@ TEST_F(CfgMgrTest, getOptionDef) {
for (uint16_t code = 100; code < 110; ++code) {
OptionDefinitionPtr def = cfg_mgr.getOptionDef("isc", code);
ASSERT_TRUE(def);
// Check that the option name is in the format of 'option-[code]'.
// That way we make sure that the options that have the same codes
// within different option spaces are different.
std::ostringstream option_name;
option_name << "option-" << code;
EXPECT_EQ(option_name.str(), def->getName());
EXPECT_EQ(code, def->getCode());
}
......@@ -147,20 +153,44 @@ TEST_F(CfgMgrTest, getOptionDef) {
for (uint16_t code = 105; code < 115; ++code) {
OptionDefinitionPtr def = cfg_mgr.getOptionDef("abcde", code);
ASSERT_TRUE(def);
// Check that the option name is in the format of 'option-other-[code]'.
// That way we make sure that the options that have the same codes
// within different option spaces are different.
std::ostringstream option_name;
option_name << "option-other-" << code;
EXPECT_EQ(option_name.str(), def->getName());
EXPECT_EQ(code, def->getCode());
}
// Check that an option definition can be added to the standard
// (dhcp4 and dhcp6) option spaces when the option code is not
// reserved by the standard option.
OptionDefinitionPtr def6(new OptionDefinition("option-foo", 79, "uint16"));
EXPECT_NO_THROW(cfg_mgr.addOptionDef(def6, "dhcp6"));
OptionDefinitionPtr def4(new OptionDefinition("option-foo", 222, "uint16"));
EXPECT_NO_THROW(cfg_mgr.addOptionDef(def4, "dhcp4"));
// Try to query the option definition from an non-existing
// option space and expect NULL pointer.
OptionDefinitionPtr def = cfg_mgr.getOptionDef("non-existing", 56);
ASSERT_FALSE(def);
EXPECT_FALSE(def);
// Try to get the non-existing option definition from an
// existing option space.
EXPECT_FALSE(cfg_mgr.getOptionDef("isc", 56));
}
// This test verifies that the function that adds new option definition
// throws exceptions when arguments are invalid.
TEST_F(CfgMgrTest, addOptionDefNegative) {
CfgMgr& cfg_mgr = CfgMgr::instance();
OptionDefinitionPtr def(new OptionDefinition("option-foo", 100, "uint16"));
// The option code 65 is reserved for standard options either in
// DHCPv4 or DHCPv6. Thus we expect that adding an option to this
// option space fails.
OptionDefinitionPtr def(new OptionDefinition("option-foo", 65, "uint16"));
// Try reserved option space names.
ASSERT_THROW(cfg_mgr.addOptionDef(def, "dhcp4"), isc::BadValue);
......@@ -170,6 +200,10 @@ TEST_F(CfgMgrTest, addOptionDefNegative) {
// Try NULL option definition.
ASSERT_THROW(cfg_mgr.addOptionDef(OptionDefinitionPtr(), "isc"),
isc::dhcp::MalformedOptionDefinition);
// Try adding option definition twice and make sure that it
// fails on the second attempt.
ASSERT_NO_THROW(cfg_mgr.addOptionDef(def, "isc"));
EXPECT_THROW(cfg_mgr.addOptionDef(def, "isc"), DuplicateOptionDefinition);
}
// This test verifies if the configuration manager is able to hold and return
......
......@@ -441,11 +441,11 @@ TEST(Subnet6Test, getOptionSingle) {
for (uint16_t code = 100; code < 110; ++code) {
std::ostringstream stream;
// First, try the invalid option space name.
Subnet::OptionDescriptor desc = subnet->getOptionSingle("isc", code);
Subnet::OptionDescriptor desc = subnet->getOptionDescriptor("isc", code);
// Returned descriptor should contain NULL option ptr.
EXPECT_FALSE(desc.option);
// Now, try the valid option space.
desc = subnet->getOptionSingle("dhcp6", code);
desc = subnet->getOptionDescriptor("dhcp6", code);
// Test that the option code matches the expected code.
ASSERT_TRUE(desc.option);
EXPECT_EQ(code, desc.option->getType());
......
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