Commit 56cfd661 authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[master] Merge branch 'trac2490'

parents ca25d3c2 139f6608
......@@ -698,11 +698,8 @@ private:
// We have exactly one option definition for the particular option code
// use it to create the option instance.
const OptionDefinitionPtr& def = *(range.first);
// getFactory should never return NULL pointer.
Option::Factory* factory = def->getFactory();
assert(factory != NULL);
try {
OptionPtr option = factory(Option::V6, option_code, binary);
OptionPtr option = def->optionFactory(Option::V6, option_code, binary);
Subnet::OptionDescriptor desc(option, false);
option_descriptor_.option = option;
option_descriptor_.persistent = false;
......
......@@ -56,12 +56,6 @@ Dhcpv6Srv::Dhcpv6Srv(uint16_t port, const char* dbconfig)
// 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();
// Port 0 is used for testing purposes. It means that the server should
// not open any sockets at all. Some tests, e.g. configuration parser,
// require Dhcpv6Srv object, but they don't really need it to do
......@@ -622,10 +616,5 @@ Dhcpv6Srv::serverReceivedPacketName(uint8_t type) {
return (UNKNOWN);
}
void
Dhcpv6Srv::initStdOptionDefs() {
LibDHCP::initStdOptionDefs(Option::V6);
}
};
};
......@@ -241,17 +241,6 @@ 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();
private:
/// @brief Allocation Engine.
/// Pointer to the allocation engine that we are currently using
......
......@@ -46,11 +46,6 @@ public:
// srv_(0) means to not open any sockets. We don't want to
// deal with sockets here, just check if configuration handling
// is sane.
// Create instances of option definitions and put them into storage.
// This is normally initialized by the server when calling run()
// run() function.
LibDHCP::initStdOptionDefs(Option::V6);
}
~Dhcp6ParserTest() {
......
......@@ -48,8 +48,12 @@ const OptionDefContainer&
LibDHCP::getOptionDefs(Option::Universe u) {
switch (u) {
case Option::V4:
initStdOptionDefs4();
return (v4option_defs_);
case Option::V6:
if (v6option_defs_.size() == 0) {
initStdOptionDefs6();
}
return (v6option_defs_);
default:
isc_throw(isc::BadValue, "invalid universe " << u << " specified");
......@@ -96,30 +100,43 @@ size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
// @todo: consider throwing exception here.
return (offset);
}
// Get the list of stdandard option definitions.
OptionDefContainer option_defs = LibDHCP::getOptionDefs(Option::V6);
// Get the search index #1. It allows to search for option definitions
// using option code.
const OptionDefContainerTypeIndex& idx = option_defs.get<1>();
// Get all options with the particular option code. Note that option code
// is non-unique within this container however at this point we expect
// to get one option definition with the particular code. If more are
// returned we report an error.
const OptionDefContainerTypeRange& range = idx.equal_range(opt_type);
// Get the number of returned option definitions for the option code.
size_t num_defs = distance(range.first, range.second);
OptionPtr opt;
switch (opt_type) {
case D6O_IA_NA:
case D6O_IA_PD:
opt = OptionPtr(new Option6IA(opt_type,
buf.begin() + offset,
buf.begin() + offset + opt_len));
break;
case D6O_IAADDR:
opt = OptionPtr(new Option6IAAddr(opt_type,
buf.begin() + offset,
buf.begin() + offset + opt_len));
break;
case D6O_ORO:
opt = OptionPtr(new Option6IntArray<uint16_t>(opt_type,
buf.begin() + offset,
buf.begin() + offset + opt_len));
break;
default:
opt = OptionPtr(new Option(Option::V6,
opt_type,
if (num_defs > 1) {
// Multiple options of the same code are not supported right now!
isc_throw(isc::Unexpected, "Internal error: multiple option definitions"
" for option type " << opt_type << " returned. Currently it is not"
" supported to initialize multiple option definitions"
" for the same option code. This will be supported once"
" support for option spaces is implemented");
} else if (num_defs == 0) {
// @todo Don't crash if definition does not exist because only a few
// option definitions are initialized right now. In the future
// we will initialize definitions for all options and we will
// remove this elseif. For now, return generic option.
opt = OptionPtr(new Option(Option::V6, opt_type,
buf.begin() + offset,
buf.begin() + offset + opt_len));
break;
} else {
// The option definition has been found. Use it to create
// the option instance from the provided buffer chunk.
const OptionDefinitionPtr& def = *(range.first);
assert(def);
opt = def->optionFactory(Option::V6, opt_type,
buf.begin() + offset,
buf.begin() + offset + opt_len);
}
// add option to options
options.insert(std::make_pair(opt_type, opt));
......@@ -228,20 +245,6 @@ void LibDHCP::OptionFactoryRegister(Option::Universe u,
return;
}
void
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");
......@@ -254,19 +257,20 @@ LibDHCP::initStdOptionDefs6() {
struct OptionParams {
std::string name;
uint16_t code;
OptionDefinition::DataType type;
OptionDataType 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 }
{ "CLIENTID", D6O_CLIENTID, OPT_BINARY_TYPE, false },
{ "SERVERID", D6O_SERVERID, OPT_BINARY_TYPE, false },
{ "IA_NA", D6O_IA_NA, OPT_RECORD_TYPE, false },
{ "IAADDR", D6O_IAADDR, OPT_RECORD_TYPE, false },
{ "ORO", D6O_ORO, OPT_UINT16_TYPE, true },
{ "ELAPSED_TIME", D6O_ELAPSED_TIME, OPT_UINT16_TYPE, false },
{ "STATUS_CODE", D6O_STATUS_CODE, OPT_RECORD_TYPE, false },
{ "RAPID_COMMIT", D6O_RAPID_COMMIT, OPT_EMPTY_TYPE, false },
{ "DNS_SERVERS", D6O_NAME_SERVERS, OPT_IPV6_ADDRESS_TYPE, true },
{ "IA_PD", D6O_IA_PD, OPT_RECORD_TYPE, false }
};
const int params_size = sizeof(params) / sizeof(params[0]);
......@@ -277,18 +281,19 @@ LibDHCP::initStdOptionDefs6() {
params[i].array));
switch(params[i].code) {
case D6O_IA_NA:
case D6O_IA_PD:
for (int j = 0; j < 3; ++j) {
definition->addRecordField(OptionDefinition::UINT32_TYPE);
definition->addRecordField(OPT_UINT32_TYPE);
}
break;
case D6O_IAADDR:
definition->addRecordField(OptionDefinition::IPV6_ADDRESS_TYPE);
definition->addRecordField(OptionDefinition::UINT32_TYPE);
definition->addRecordField(OptionDefinition::UINT32_TYPE);
definition->addRecordField(OPT_IPV6_ADDRESS_TYPE);
definition->addRecordField(OPT_UINT32_TYPE);
definition->addRecordField(OPT_UINT32_TYPE);
break;
case D6O_STATUS_CODE:
definition->addRecordField(OptionDefinition::UINT16_TYPE);
definition->addRecordField(OptionDefinition::STRING_TYPE);
definition->addRecordField(OPT_UINT16_TYPE);
definition->addRecordField(OPT_STRING_TYPE);
break;
default:
// The default case is intentionally left empty
......@@ -298,9 +303,12 @@ LibDHCP::initStdOptionDefs6() {
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());
// This is unlikely event that validation fails and may
// be only caused by programming error. To guarantee the
// data consistency we clear all option definitions that
// have been added so far and pass the exception forward.
v6option_defs_.clear();
throw;
}
v6option_defs_.push_back(definition);
}
......
......@@ -33,7 +33,14 @@ public:
/// @brief Return collection of option definitions.
///
/// Method returns the collection of DHCP standard DHCP
/// option definitions.
/// @todo DHCPv4 option definitions are not implemented. For now
/// this function will throw isc::NotImplemented in case of attempt
/// to get option definitions for V4 universe.
///
/// @param u universe of the options (V4 or V6).
///
/// @return collection of option definitions.
static const OptionDefContainer& getOptionDefs(Option::Universe u);
......@@ -113,21 +120,6 @@ public:
uint16_t type,
Option::Factory * factory);
/// 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.
......@@ -144,9 +136,9 @@ private:
///
/// 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.
/// @throw MalformedOptionDefinition if any of the definitions
/// is incorect. This is a programming error.
static void initStdOptionDefs6();
/// pointers to factories that produce DHCPv6 options
......
......@@ -48,7 +48,7 @@ public:
/// as template parameter is not a supported integer type.
Option6Int(uint16_t type, T value)
: Option(Option::V6, type), value_(value) {
if (!OptionDataTypes<T>::valid) {
if (!OptionDataTypeTraits<T>::integer_type) {
isc_throw(dhcp::InvalidDataType, "non-integer type");
}
}
......@@ -69,7 +69,7 @@ public:
Option6Int(uint16_t type, OptionBufferConstIter begin,
OptionBufferConstIter end)
: Option(Option::V6, type) {
if (!OptionDataTypes<T>::valid) {
if (!OptionDataTypeTraits<T>::integer_type) {
isc_throw(dhcp::InvalidDataType, "non-integer type");
}
unpack(begin, end);
......@@ -91,7 +91,7 @@ public:
// order to the provided buffer. The same functions can be safely used
// for either unsiged or signed integers so there is not need to create
// special cases for intX_t types.
switch (OptionDataTypes<T>::len) {
switch (OptionDataTypeTraits<T>::len) {
case 1:
buf.writeUint8(value_);
break;
......@@ -130,7 +130,7 @@ public:
// order from the provided buffer. The same functions can be safely used
// for either unsiged or signed integers so there is not need to create
// special cases for intX_t types.
int data_size_len = OptionDataTypes<T>::len;
int data_size_len = OptionDataTypeTraits<T>::len;
switch (data_size_len) {
case 1:
value_ = *begin;
......@@ -145,9 +145,9 @@ public:
isc_throw(dhcp::InvalidDataType, "non-integer type");
}
// Use local variable to set a new value for this iterator.
// When using OptionDataTypes<T>::len directly some versions
// When using OptionDataTypeTraits<T>::len directly some versions
// of clang complain about unresolved reference to
// OptionDataTypes structure during linking.
// OptionDataTypeTraits structure during linking.
begin += data_size_len;
LibDHCP::unpackOptions6(OptionBuffer(begin, end), options_);
}
......
......@@ -58,7 +58,7 @@ public:
Option6IntArray(uint16_t type)
: Option(Option::V6, type),
values_(0) {
if (!OptionDataTypes<T>::valid) {
if (!OptionDataTypeTraits<T>::integer_type) {
isc_throw(dhcp::InvalidDataType, "non-integer type");
}
}
......@@ -74,7 +74,7 @@ public:
/// as template parameter is not a supported integer type.
Option6IntArray(uint16_t type, const OptionBuffer& buf)
: Option(Option::V6, type) {
if (!OptionDataTypes<T>::valid) {
if (!OptionDataTypeTraits<T>::integer_type) {
isc_throw(dhcp::InvalidDataType, "non-integer type");
}
unpack(buf.begin(), buf.end());
......@@ -97,7 +97,7 @@ public:
Option6IntArray(uint16_t type, OptionBufferConstIter begin,
OptionBufferConstIter end)
: Option(Option::V6, type) {
if (!OptionDataTypes<T>::valid) {
if (!OptionDataTypeTraits<T>::integer_type) {
isc_throw(dhcp::InvalidDataType, "non-integer type");
}
unpack(begin, end);
......@@ -120,7 +120,7 @@ public:
// order to the provided buffer. The same functions can be safely used
// for either unsiged or signed integers so there is not need to create
// special cases for intX_t types.
switch (OptionDataTypes<T>::len) {
switch (OptionDataTypeTraits<T>::len) {
case 1:
buf.writeUint8(values_[i]);
break;
......@@ -166,7 +166,7 @@ public:
// order from the provided buffer. The same functions can be safely used
// for either unsiged or signed integers so there is not need to create
// special cases for intX_t types.
int data_size_len = OptionDataTypes<T>::len;
int data_size_len = OptionDataTypeTraits<T>::len;
switch (data_size_len) {
case 1:
values_.push_back(*begin);
......@@ -181,9 +181,9 @@ public:
isc_throw(dhcp::InvalidDataType, "non-integer type");
}
// Use local variable to set a new value for this iterator.
// When using OptionDataTypes<T>::len directly some versions
// When using OptionDataTypeTraits<T>::len directly some versions
// of clang complain about unresolved reference to
// OptionDataTypes structure during linking.
// OptionDataTypeTraits structure during linking.
begin += data_size_len;
}
// We do not unpack sub-options here because we have array-type option.
......
......@@ -15,6 +15,7 @@
#ifndef OPTION_DATA_TYPES_H
#define OPTION_DATA_TYPES_H
#include <asiolink/io_address.h>
#include <exceptions/exceptions.h>
#include <stdint.h>
......@@ -29,59 +30,147 @@ public:
isc::Exception(file, line, what) { };
};
/// @brief Trait class for integer data types supported in DHCP option definitions.
/// @brief Exception to be thrown when cast to the data type was unsuccessful.
class BadDataTypeCast : public Exception {
public:
BadDataTypeCast(const char* file, size_t line, const char* what) :
isc::Exception(file, line, what) { };
};
/// @brief Data types of DHCP option fields.
enum OptionDataType {
OPT_EMPTY_TYPE,
OPT_BINARY_TYPE,
OPT_BOOLEAN_TYPE,
OPT_INT8_TYPE,
OPT_INT16_TYPE,
OPT_INT32_TYPE,
OPT_UINT8_TYPE,
OPT_UINT16_TYPE,
OPT_UINT32_TYPE,
OPT_ANY_ADDRESS_TYPE,
OPT_IPV4_ADDRESS_TYPE,
OPT_IPV6_ADDRESS_TYPE,
OPT_STRING_TYPE,
OPT_FQDN_TYPE,
OPT_RECORD_TYPE,
OPT_UNKNOWN_TYPE
};
/// @brief Trait class for data types supported in DHCP option definitions.
///
/// This is useful to check whether the type specified as template parameter
/// is supported by classes like Option6Int, Option6IntArray and some template
/// factory functions in OptionDefinition class.
template<typename T>
struct OptionDataTypes {
struct OptionDataTypeTraits {
static const bool valid = false;
static const int len = 0;
static const bool integer_type = false;
static const OptionDataType type = OPT_UNKNOWN_TYPE;
};
/// binary type is supported
template<>
struct OptionDataTypeTraits<OptionBuffer> {
static const bool valid = true;
static const int len = sizeof(OptionBuffer);
static const bool integer_type = false;
static const OptionDataType type = OPT_BINARY_TYPE;
};
/// bool type is supported
template<>
struct OptionDataTypeTraits<bool> {
static const bool valid = true;
static const int len = sizeof(bool);
static const bool integer_type = false;
static const OptionDataType type = OPT_BOOLEAN_TYPE;
};
/// int8_t type is supported.
template<>
struct OptionDataTypes<int8_t> {
struct OptionDataTypeTraits<int8_t> {
static const bool valid = true;
static const int len = 1;
static const bool integer_type = true;
static const OptionDataType type = OPT_INT8_TYPE;
};
/// int16_t type is supported.
template<>
struct OptionDataTypes<int16_t> {
struct OptionDataTypeTraits<int16_t> {
static const bool valid = true;
static const int len = 2;
static const bool integer_type = true;
static const OptionDataType type = OPT_INT16_TYPE;
};
/// int32_t type is supported.
template<>
struct OptionDataTypes<int32_t> {
struct OptionDataTypeTraits<int32_t> {
static const bool valid = true;
static const int len = 4;
static const bool integer_type = true;
static const OptionDataType type = OPT_INT32_TYPE;
};
/// uint8_t type is supported.
template<>
struct OptionDataTypes<uint8_t> {
struct OptionDataTypeTraits<uint8_t> {
static const bool valid = true;
static const int len = 1;
static const bool integer_type = true;
static const OptionDataType type = OPT_UINT8_TYPE;
};
/// uint16_t type is supported.
template<>
struct OptionDataTypes<uint16_t> {
struct OptionDataTypeTraits<uint16_t> {
static const bool valid = true;
static const int len = 2;
static const bool integer_type = true;
static const OptionDataType type = OPT_UINT16_TYPE;
};
/// uint32_t type is supported.
template<>
struct OptionDataTypes<uint32_t> {
struct OptionDataTypeTraits<uint32_t> {
static const bool valid = true;
static const int len = 4;
static const bool integer_type = true;
static const OptionDataType type = OPT_UINT32_TYPE;
};
/// IPv4 and IPv6 address type is supported
template<>
struct OptionDataTypeTraits<asiolink::IOAddress> {
static const bool valid = true;
// The len value is used to determine the size of the data
// to be written to an option buffer. IOAddress object may
// either represent an IPv4 or IPv6 addresses which have
// different lengths. Thus we can't put fixed value here.
// The length of a data to be written into an option buffer
// have to be determined in the runtime for a particular
// IOAddress object. Thus setting len to zero.
static const int len = 0;
static const bool integer_type = false;
static const OptionDataType type = OPT_ANY_ADDRESS_TYPE;
};
/// string type is supported
template<>
struct OptionDataTypeTraits<std::string> {
static const bool valid = true;
// The len value is used to determine the size of the data
// to be written to an option buffer. For strings this
// size is unknown until we actually deal with the particular
// string to be written. Thus setting it to zero.
static const int len = 0;
static const bool integer_type = false;
static const OptionDataType type = OPT_STRING_TYPE;
};
} // isc::dhcp namespace
} // isc namespace
......
This diff is collapsed.
......@@ -27,6 +27,21 @@
namespace isc {
namespace dhcp {
/// @brief Exception to be thrown when invalid option value has been
/// specified for a particular option definition.
class InvalidOptionValue : public Exception {
public:
InvalidOptionValue(const char* file, size_t line, const char* what) :
isc::Exception(file, line, what) { };
};
/// @brief Exception to be thrown when option definition is invalid.
class MalformedOptionDefinition : public Exception {
public:
MalformedOptionDefinition(const char* file, size_t line, const char* what) :
isc::Exception(file, line, what) { };
};
/// @brief Forward declaration to OptionDefinition.
class OptionDefinition;
......@@ -82,7 +97,7 @@ class Option6IntArray;
///
/// 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 OptioDefinition::addRecordField.
/// are specified using \ref OptionDefinition::addRecordField.
///
/// When the OptionDefinition object has been sucessfully created, it can be
/// queried to return the appropriate option factory function for the specified
......@@ -111,33 +126,14 @@ class Option6IntArray;
class OptionDefinition {
public:
/// Data types of DHCP option fields.
enum DataType {
EMPTY_TYPE,
BINARY_TYPE,
BOOLEAN_TYPE,
INT8_TYPE,
INT16_TYPE,
INT32_TYPE,
UINT8_TYPE,
UINT16_TYPE,
UINT32_TYPE,
IPV4_ADDRESS_TYPE,
IPV6_ADDRESS_TYPE,
STRING_TYPE,
FQDN_TYPE,
RECORD_TYPE,
UNKNOWN_TYPE
};
/// List of fields within the record.
typedef std::vector<DataType> RecordFieldsCollection;
typedef std::vector<OptionDataType> RecordFieldsCollection;
/// Const iterator for record data fields.
typedef std::vector<DataType>::const_iterator RecordFieldsConstIter;
typedef std::vector<OptionDataType>::const_iterator RecordFieldsConstIter;
private: