Commit 71e25eb8 authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[master] Merge branch 'trac2317'

Conflicts:
	src/bin/dhcp6/dhcp6_srv.cc
	src/lib/dhcpsrv/Makefile.am
	src/lib/dhcpsrv/cfgmgr.h
parents 65eb1db4 5a326976
This diff is collapsed.
......@@ -28,20 +28,6 @@ namespace dhcp {
class Dhcpv4Srv;
/// An exception that is thrown if an error occurs while configuring an
/// @c Dhcpv4Srv object.
class Dhcp4ConfigError : public isc::Exception {
public:
/// @brief constructor
///
/// @param file name of the file, where exception occurred
/// @param line line of the file, where exception occurred
/// @param what text description of the issue that caused exception
Dhcp4ConfigError(const char* file, size_t line, const char* what)
: isc::Exception(file, line, what) {}
};
/// @brief Configure DHCPv4 server (@c Dhcpv4Srv) with a set of configuration values.
///
/// This function parses configuration information stored in @c config_set
......
......@@ -19,6 +19,7 @@
#include <cc/session.h>
#include <config/ccsession.h>
#include <dhcp/iface_mgr.h>
#include <dhcpsrv/dhcp_config_parser.h>
#include <dhcp4/ctrl_dhcp4_srv.h>
#include <dhcp4/dhcp4_log.h>
#include <dhcp4/spec_config.h>
......@@ -121,7 +122,7 @@ void ControlledDhcpv4Srv::establishSession() {
try {
configureDhcp4Server(*this, config_session_->getFullConfig());
} catch (const Dhcp4ConfigError& ex) {
} catch (const DhcpConfigError& ex) {
LOG_ERROR(dhcp4_logger, DHCP4_CONFIG_LOAD_FAIL).arg(ex.what());
}
......
......@@ -34,6 +34,56 @@
"item_default": 4000
},
{ "item_name": "option-def",
"item_type": "list",
"item_optional": false,
"item_default": [],
"list_item_spec":
{
"item_name": "single-option-def",
"item_type": "map",
"item_optional": false,
"item_default": {},
"map_item_spec": [
{
"item_name": "name",
"item_type": "string",
"item_optional": false,
"item_default": ""
},
{ "item_name": "code",
"item_type": "integer",
"item_optional": false,
"item_default": 0,
},
{ "item_name": "type",
"item_type": "string",
"item_optional": false,
"item_default": "",
},
{ "item_name": "array",
"item_type": "boolean",
"item_optional": false,
"item_default": False
},
{ "item_name": "record_types",
"item_type": "string",
"item_optional": false,
"item_default": "",
},
{ "item_name": "space",
"item_type": "string",
"item_optional": false,
"item_default": ""
} ]
}
},
{ "item_name": "option-data",
"item_type": "list",
"item_optional": false,
......@@ -66,6 +116,11 @@
"item_type": "boolean",
"item_optional": false,
"item_default": False
},
{ "item_name": "space",
"item_type": "string",
"item_optional": false,
"item_default": "dhcp4"
} ]
}
},
......@@ -151,6 +206,11 @@
"item_type": "boolean",
"item_optional": false,
"item_default": False
},
{ "item_name": "space",
"item_type": "string",
"item_optional": false,
"item_default": "dhcp4"
} ]
}
} ]
......
This diff is collapsed.
......@@ -27,20 +27,6 @@ namespace dhcp {
class Dhcpv6Srv;
/// An exception that is thrown if an error occurs while configuring an
/// @c Dhcpv6Srv object.
class Dhcp6ConfigError : public isc::Exception {
public:
/// @brief constructor
///
/// @param file name of the file, where exception occurred
/// @param line line of the file, where exception occurred
/// @param what text description of the issue that caused exception
Dhcp6ConfigError(const char* file, size_t line, const char* what)
: isc::Exception(file, line, what) {}
};
/// @brief Configures DHCPv6 server
///
/// This function is called every time a new configuration is received. The extra
......
......@@ -19,6 +19,7 @@
#include <cc/session.h>
#include <config/ccsession.h>
#include <dhcp/iface_mgr.h>
#include <dhcpsrv/dhcp_config_parser.h>
#include <dhcp6/config_parser.h>
#include <dhcp6/ctrl_dhcp6_srv.h>
#include <dhcp6/dhcp6_log.h>
......@@ -121,7 +122,7 @@ void ControlledDhcpv6Srv::establishSession() {
try {
configureDhcp6Server(*this, config_session_->getFullConfig());
} catch (const Dhcp6ConfigError& ex) {
} catch (const DhcpConfigError& ex) {
LOG_ERROR(dhcp6_logger, DHCP6_CONFIG_LOAD_FAIL).arg(ex.what());
}
......
......@@ -40,6 +40,56 @@
"item_default": 4000
},
{ "item_name": "option-def",
"item_type": "list",
"item_optional": false,
"item_default": [],
"list_item_spec":
{
"item_name": "single-option-def",
"item_type": "map",
"item_optional": false,
"item_default": {},
"map_item_spec": [
{
"item_name": "name",
"item_type": "string",
"item_optional": false,
"item_default": ""
},
{ "item_name": "code",
"item_type": "integer",
"item_optional": false,
"item_default": 0,
},
{ "item_name": "type",
"item_type": "string",
"item_optional": false,
"item_default": "",
},
{ "item_name": "array",
"item_type": "boolean",
"item_optional": false,
"item_default": False
},
{ "item_name": "record_types",
"item_type": "string",
"item_optional": false,
"item_default": "",
},
{ "item_name": "space",
"item_type": "string",
"item_optional": false,
"item_default": ""
} ]
}
},
{ "item_name": "option-data",
"item_type": "list",
"item_optional": false,
......@@ -72,6 +122,11 @@
"item_type": "boolean",
"item_optional": false,
"item_default": False
},
{ "item_name": "space",
"item_type": "string",
"item_optional": false,
"item_default": "dhcp6"
} ]
}
},
......@@ -162,6 +217,11 @@
"item_type": "boolean",
"item_optional": false,
"item_default": False
},
{ "item_name": "space",
"item_type": "string",
"item_optional": false,
"item_default": "dhcp6"
} ]
}
} ]
......
......@@ -405,7 +405,7 @@ void Dhcpv6Srv::sanityCheck(const Pkt6Ptr& pkt, RequirementLevel clientid,
Option::OptionCollection server_ids = pkt->getOptions(D6O_SERVERID);
switch (serverid) {
case FORBIDDEN:
if (server_ids.size() > 0) {
if (!server_ids.empty()) {
isc_throw(RFCViolation, "Server-id option was not expected, but "
<< server_ids.size() << " received in " << pkt->getName());
}
......
......@@ -370,14 +370,16 @@ TEST_F(Dhcpv6SrvTest, advertiseOptions) {
" \"pool\": [ \"2001:db8:1::/64\" ],"
" \"subnet\": \"2001:db8:1::/48\", "
" \"option-data\": [ {"
" \"name\": \"OPTION_DNS_SERVERS\","
" \"name\": \"dns-servers\","
" \"space\": \"dhcp6\","
" \"code\": 23,"
" \"data\": \"2001:db8:1234:FFFF::1, 2001:db8:1234:FFFF::2\","
" \"csv-format\": True"
" },"
" {"
" \"name\": \"OPTION_FOO\","
" \"code\": 1000,"
" \"name\": \"subscriber-id\","
" \"space\": \"dhcp6\","
" \"code\": 38,"
" \"data\": \"1234\","
" \"csv-format\": False"
" } ]"
......@@ -406,18 +408,18 @@ TEST_F(Dhcpv6SrvTest, advertiseOptions) {
// check if we get response at all
ASSERT_TRUE(adv);
// We have not requested option with code 1000 so it should not
// We have not requested any options so they should not
// be included in the response.
ASSERT_FALSE(adv->getOption(1000));
ASSERT_FALSE(adv->getOption(D6O_SUBSCRIBER_ID));
ASSERT_FALSE(adv->getOption(D6O_NAME_SERVERS));
// Let's now request option with code 1000.
// We expect that server will include this option in its reply.
// Let's now request some options. We expect that the server
// will include them in its response.
boost::shared_ptr<OptionIntArray<uint16_t> >
option_oro(new OptionIntArray<uint16_t>(Option::V6, D6O_ORO));
// Create vector with two option codes.
std::vector<uint16_t> codes(2);
codes[0] = 1000;
codes[0] = D6O_SUBSCRIBER_ID;
codes[1] = D6O_NAME_SERVERS;
// Pass this code to option.
option_oro->setValues(codes);
......@@ -442,7 +444,7 @@ TEST_F(Dhcpv6SrvTest, advertiseOptions) {
// There is a dummy option with code 1000 we requested from a server.
// Expect that this option is in server's response.
tmp = adv->getOption(1000);
tmp = adv->getOption(D6O_SUBSCRIBER_ID);
ASSERT_TRUE(tmp);
// Check that the option contains valid data (from configuration).
......
......@@ -23,6 +23,8 @@
#include <dhcp/option_int_array.h>
#include <util/encode/hex.h>
#include <util/strutil.h>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/predicate.hpp>
using namespace std;
using namespace isc::util;
......@@ -207,16 +209,29 @@ OptionDefinition::sanityCheckUniverse(const Option::Universe expected_universe,
void
OptionDefinition::validate() const {
using namespace boost::algorithm;
std::ostringstream err_str;
if (name_.empty()) {
// Option name must not be empty.
err_str << "option name must not be empty.";
} else if (name_.find(" ") != string::npos) {
// Option name must not contain spaces.
err_str << "option name must not contain spaces.";
// Allowed characters in the option name are: lower or
// upper case letters, digits, underscores and hyphens.
// Empty option spaces are not allowed.
if (!all(name_, boost::is_from_range('a', 'z') ||
boost::is_from_range('A', 'Z') ||
boost::is_digit() ||
boost::is_any_of(std::string("-_"))) ||
name_.empty() ||
// Hyphens and underscores are not allowed at the beginning
// and at the end of the option name.
all(find_head(name_, 1), boost::is_any_of(std::string("-_"))) ||
all(find_tail(name_, 1), boost::is_any_of(std::string("-_")))) {
err_str << "invalid option name '" << name_ << "'";
} else if (type_ >= OPT_UNKNOWN_TYPE) {
// Option definition must be of a known type.
err_str << "option type value " << type_ << " is out of range.";
} else if (array_type_) {
if (type_ == OPT_STRING_TYPE) {
// Array of strings is not allowed because there is no way
......@@ -225,9 +240,12 @@ OptionDefinition::validate() const {
err_str << "array of strings is not a valid option definition.";
} else if (type_ == OPT_BINARY_TYPE) {
err_str << "array of binary values is not a valid option definition.";
} else if (type_ == OPT_EMPTY_TYPE) {
err_str << "array of empty value is not a valid option definition.";
}
} else if (type_ == OPT_RECORD_TYPE) {
// At least two data fields should be added to the record. Otherwise
// non-record option definition could be used.
......@@ -235,6 +253,7 @@ OptionDefinition::validate() const {
err_str << "invalid number of data fields: " << getRecordFields().size()
<< " specified for the option of type 'record'. Expected at"
<< " least 2 fields.";
} else {
// If the number of fields is valid we have to check if their order
// is valid too. We check that string or binary data fields are not
......
......@@ -164,29 +164,55 @@ TEST_F(OptionDefinitionTest, validate) {
EXPECT_THROW(opt_def5.validate(), MalformedOptionDefinition);
// Option name must not contain spaces.
OptionDefinition opt_def6("OPTION CLIENTID", D6O_CLIENTID, "string", true);
OptionDefinition opt_def6("OPTION CLIENTID", D6O_CLIENTID, "string");
EXPECT_THROW(opt_def6.validate(), MalformedOptionDefinition);
// Option name may contain lower case letters.
OptionDefinition opt_def7("option_clientid", D6O_CLIENTID, "string");
EXPECT_NO_THROW(opt_def7.validate());
// Using digits in option name is legal.
OptionDefinition opt_def8("option_123", D6O_CLIENTID, "string");
EXPECT_NO_THROW(opt_def8.validate());
// Using hyphen is legal.
OptionDefinition opt_def9("option-clientid", D6O_CLIENTID, "string");
EXPECT_NO_THROW(opt_def9.validate());
// Using hyphen or undescore at the beginning or at the end
// of the option name is not allowed.
OptionDefinition opt_def10("-option-clientid", D6O_CLIENTID, "string");
EXPECT_THROW(opt_def10.validate(), MalformedOptionDefinition);
OptionDefinition opt_def11("_option-clientid", D6O_CLIENTID, "string");
EXPECT_THROW(opt_def11.validate(), MalformedOptionDefinition);
OptionDefinition opt_def12("option-clientid_", D6O_CLIENTID, "string");
EXPECT_THROW(opt_def12.validate(), MalformedOptionDefinition);
OptionDefinition opt_def13("option-clientid-", D6O_CLIENTID, "string");
EXPECT_THROW(opt_def13.validate(), MalformedOptionDefinition);
// Having array of strings does not make sense because there is no way
// to determine string's length.
OptionDefinition opt_def7("OPTION_CLIENTID", D6O_CLIENTID, "string", true);
EXPECT_THROW(opt_def7.validate(), MalformedOptionDefinition);
OptionDefinition opt_def14("OPTION_CLIENTID", D6O_CLIENTID, "string", true);
EXPECT_THROW(opt_def14.validate(), MalformedOptionDefinition);
// It does not make sense to have string field within the record before
// other fields because there is no way to determine the length of this
// string and thus there is no way to determine where the other field
// begins.
OptionDefinition opt_def8("OPTION_STATUS_CODE", D6O_STATUS_CODE,
"record");
opt_def8.addRecordField("string");
opt_def8.addRecordField("uint16");
EXPECT_THROW(opt_def8.validate(), MalformedOptionDefinition);
OptionDefinition opt_def15("OPTION_STATUS_CODE", D6O_STATUS_CODE,
"record");
opt_def15.addRecordField("string");
opt_def15.addRecordField("uint16");
EXPECT_THROW(opt_def15.validate(), MalformedOptionDefinition);
// ... but it is ok if the string value is the last one.
OptionDefinition opt_def9("OPTION_STATUS_CODE", D6O_STATUS_CODE,
"record");
opt_def9.addRecordField("uint8");
opt_def9.addRecordField("string");
OptionDefinition opt_def16("OPTION_STATUS_CODE", D6O_STATUS_CODE,
"record");
opt_def16.addRecordField("uint8");
opt_def16.addRecordField("string");
}
......
......@@ -43,6 +43,7 @@ if HAVE_MYSQL
libb10_dhcpsrv_la_SOURCES += mysql_lease_mgr.cc mysql_lease_mgr.h
endif
libb10_dhcpsrv_la_SOURCES += option_space.cc option_space.h
libb10_dhcpsrv_la_SOURCES += option_space_container.h
libb10_dhcpsrv_la_SOURCES += pool.cc pool.h
libb10_dhcpsrv_la_SOURCES += subnet.cc subnet.h
libb10_dhcpsrv_la_SOURCES += triplet.h
......
......@@ -86,30 +86,15 @@ CfgMgr::addOptionDef(const OptionDefinitionPtr& def,
<< option_space << "'.");
}
// Get existing option definitions for the option space.
OptionDefContainerPtr defs = getOptionDefs(option_space);
// getOptionDefs always returns a valid pointer to
// the container. Let's make an assert to make sure.
assert(defs);
// Actually add the new definition.
defs->push_back(def);
option_def_spaces_[option_space] = defs;
// Actually add a new item.
option_def_spaces_.addItem(def, option_space);
}
OptionDefContainerPtr
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 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.
if (defs == option_def_spaces_.end()) {
return (OptionDefContainerPtr(new OptionDefContainer()));
}
// If option definitions found, return them.
return (defs->second);
return (option_def_spaces_.getItems(option_space));
}
OptionDefinitionPtr
......@@ -229,7 +214,7 @@ void CfgMgr::addSubnet4(const Subnet4Ptr& subnet) {
}
void CfgMgr::deleteOptionDefs() {
option_def_spaces_.clear();
option_def_spaces_.clearItems();
}
void CfgMgr::deleteSubnets4() {
......
......@@ -19,6 +19,7 @@
#include <dhcp/option.h>
#include <dhcp/option_definition.h>
#include <dhcpsrv/option_space.h>
#include <dhcpsrv/option_space_container.h>
#include <dhcpsrv/pool.h>
#include <dhcpsrv/subnet.h>
#include <util/buffer.h>
......@@ -66,6 +67,7 @@ namespace dhcp {
/// Parameter inheritance is likely to be implemented in configuration handling
/// routines, so there is no storage capability in a global scope for
/// subnet-specific parameters.
///
/// @todo: Implement Subnet4 support (ticket #2237)
/// @todo: Implement option definition support
/// @todo: Implement parameter inheritance
......@@ -250,15 +252,12 @@ 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, OptionDefContainerPtr> OptionDefsMap;
/// A map containing option definitions for different option spaces.
/// The map key holds an option space name.
OptionDefsMap option_def_spaces_;
/// @brief A collection of option definitions.
///
/// A collection of option definitions that can be accessed
/// using option space name they belong to.
OptionSpaceContainer<OptionDefContainer,
OptionDefinitionPtr> option_def_spaces_;
/// @brief Container for defined DHCPv6 option spaces.
OptionSpaceCollection spaces6_;
......
......@@ -18,6 +18,20 @@
namespace isc {
namespace dhcp {
/// An exception that is thrown if an error occurs while configuring
/// DHCP server.
class DhcpConfigError : public isc::Exception {
public:
/// @brief constructor
///
/// @param file name of the file, where exception occurred
/// @param line line of the file, where exception occurred
/// @param what text description of the issue that caused exception
DhcpConfigError(const char* file, size_t line, const char* what)
: isc::Exception(file, line, what) {}
};
/// @brief Forward declaration to DhcpConfigParser class.
///
/// It is only needed here to define types that are
......@@ -105,6 +119,34 @@ public:
/// This method is expected to be called after @c build(), and only once.
/// The result is undefined otherwise.
virtual void commit() = 0;
protected:
/// @brief Return the parsed entry from the provided storage.
///
/// This method returns the parsed entry from the provided
/// storage. If the entry is not found, then exception is
/// thrown.
///
/// @param param_id name of the configuration entry.
/// @param storage storage where the entry should be searched.
/// @tparam ReturnType type of the returned value.
/// @tparam StorageType type of the storage.
///
/// @throw DhcpConfigError if the entry has not been found
/// in the storage.
template<typename ReturnType, typename StorageType>
static ReturnType getParam(const std::string& param_id,
const StorageType& storage) {
typename StorageType::const_iterator param = storage.find(param_id);
if (param == storage.end()) {
isc_throw(DhcpConfigError, "missing parameter '"
<< param_id << "'");
}
ReturnType value = param->second;
return (value);
}
};
......
// Copyright (C) 2013 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
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#ifndef OPTION_SPACE_CONTAINER_H
#define OPTION_SPACE_CONTAINER_H
#include <list>
#include <string>
namespace isc {
namespace dhcp {
/// @brief Simple container for option spaces holding various items.
///
/// This helper class is used to store items of various types in
/// that are grouped by option space names. Each option space is
/// mapped to a container that holds items which specifically can
/// be OptionDefinition objects or Subnet::OptionDescriptor structures.
///
/// @tparam ContainerType of the container holding items within