Commit 3c35688d authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[2304] Added basic implementation of OptionDefinition class and unit tests.

parent f5811cdf
......@@ -21,10 +21,12 @@ libb10_dhcp___la_SOURCES += iface_mgr_linux.cc
libb10_dhcp___la_SOURCES += iface_mgr_bsd.cc
libb10_dhcp___la_SOURCES += iface_mgr_sun.cc
libb10_dhcp___la_SOURCES += option.cc option.h
libb10_dhcp___la_SOURCES += option_definition.cc option_definition.h
libb10_dhcp___la_SOURCES += option6_ia.cc option6_ia.h
libb10_dhcp___la_SOURCES += option6_iaaddr.cc option6_iaaddr.h
libb10_dhcp___la_SOURCES += option6_addrlst.cc option6_addrlst.h
libb10_dhcp___la_SOURCES += option4_addrlst.cc option4_addrlst.h
libb10_dhcp___la_SOURCES += option6_int.h
libb10_dhcp___la_SOURCES += dhcp6.h dhcp4.h
libb10_dhcp___la_SOURCES += pkt6.cc pkt6.h
libb10_dhcp___la_SOURCES += pkt4.cc pkt4.h
......
......@@ -20,6 +20,7 @@
#include <vector>
#include <boost/shared_ptr.hpp>
#include <boost/shared_array.hpp>
#include <asiolink/io_address.h>
#include <util/buffer.h>
#include <dhcp/option.h>
......
// Copyright (C) 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
// 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_INT6_H_
#define OPTION_INT6_H_
#include <stdint.h>
#include <limits>
#include <util/io_utilities.h>
#include "dhcp/option.h"
namespace isc {
namespace dhcp {
template<typename T>
class OptionInt6: public Option {
public:
OptionInt6(uint16_t type, T value)
: Option(Option::V6, type), value_(value) {
}
OptionInt6(uint16_t type, OptionBufferConstIter begin,
OptionBufferConstIter end)
: Option(Option::V6, type) {
unpack(begin, end);
}
/// Writes option in wire-format to buf, returns pointer to first unused
/// byte after stored option.
///
/// @param buf buffer (option will be stored here)
void pack(isc::util::OutputBuffer& buf) {
if (!std::numeric_limits<T>::is_integer) {
isc_throw(isc::BadValue, "");
}
buf.writeUint16(type_);
buf.writeUint16(len() - OPTION6_HDR_LEN);
switch (sizeof(T)) {
case 1:
buf.writeUint8(value_);
break;
case 2:
buf.writeUint16(value_);
break;
case 4:
buf.writeUint32(value_);
break;
default:
isc_throw(isc::BadValue, "");
}
}
/// @brief Parses received buffer
///
/// Parses received buffer and returns offset to the first unused byte after
/// parsed option.
///
/// @param begin iterator to first byte of option data
/// @param end iterator to end of option data (first byte after option end)
virtual void unpack(OptionBufferConstIter begin, OptionBufferConstIter end) {
if (distance(begin, end) < sizeof(T)) {
isc_throw(OutOfRange, "Option " << type_ << " truncated");
}
switch (sizeof(T)) {
case 1:
value_ = *begin;
break;
case 2:
value_ = isc::util::readUint16( &(*begin) );
break;
case 4:
value_ = isc::util::readUint32( &(*begin) );
break;
default:
isc_throw(isc::BadValue, "");
}
LibDHCP::unpackOptions6(OptionBuffer(begin, end), options_);
}
void setValue(T value) { value_ = value; }
T getValue() const { return value_; }
/// @brief returns complete length of option
///
/// Returns length of this option, including option header and suboptions
///
/// @return length of this option
virtual uint16_t len() {
uint16_t length = OPTION6_HDR_LEN + sizeof(T);
// length of all suboptions
for (Option::OptionCollection::iterator it = options_.begin();
it != options_.end();
++it) {
length += (*it).second->len();
}
return (length);
}
private:
T value_;
};
} // isc::dhcp namespace
} // isc namespace
#endif /* OPTION_IA_H_ */
// Copyright (C) 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
// 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.
#include "dhcp/option_definition.h"
#include "dhcp/option4_addrlst.h"
#include "dhcp/option6_addrlst.h"
#include "dhcp/option6_int.h"
using namespace std;
using namespace isc::util;
namespace isc {
namespace dhcp {
OptionDefinition::DataTypeUtil::DataTypeUtil() {
data_types_["empty"] = EMPTY_TYPE;
data_types_["boolean"] = BOOLEAN_TYPE;
data_types_["int8"] = INT8_TYPE;
data_types_["int16"] = INT16_TYPE;
data_types_["int32"] = INT32_TYPE;
data_types_["uint8"] = UINT8_TYPE;
data_types_["uint16"] = UINT16_TYPE;
data_types_["uint32"] = UINT32_TYPE;
data_types_["ipv4-address"] = IPV4_ADDRESS_TYPE;
data_types_["ipv6-address"] = IPV6_ADDRESS_TYPE;
data_types_["string"] = STRING_TYPE;
data_types_["fqdn"] = FQDN_TYPE;
data_types_["record"] = RECORD_TYPE;
}
OptionDefinition::DataType
OptionDefinition::DataTypeUtil::getDataType(const std::string& data_type) {
std::map<std::string, DataType>::const_iterator data_type_it =
data_types_.find(data_type);
if (data_type_it != data_types_.end()) {
return (data_type_it->second);
}
return UNKNOWN_TYPE;
}
OptionDefinition::OptionDefinition(const std::string& name,
const uint16_t code,
const std::string& type,
const bool array_type /* = false */)
: name_(name),
code_(code),
type_(UNKNOWN_TYPE),
array_type_(array_type) {
// Data type is held as enum value by this class.
// Use the provided option type string to get the
// corresponding enum value.
type_ = DataTypeUtil::instance().getDataType(type);
}
OptionDefinition::OptionDefinition(const std::string& name,
const uint16_t code,
const DataType type,
const bool array_type /* = false */)
: name_(name),
code_(code),
type_(type),
array_type_(array_type) {
}
void
OptionDefinition::addRecordField(const std::string& data_type_name) {
DataType data_type = DataTypeUtil::instance().getDataType(data_type_name);
addRecordField(data_type);
}
void
OptionDefinition::addRecordField(const DataType data_type) {
if (type_ != RECORD_TYPE) {
isc_throw(isc::InvalidOperation, "'record' option type must be used"
" to add data fields to the record");
}
if (data_type >= UNKNOWN_TYPE) {
isc_throw(isc::BadValue, "attempted to add invalid data type to the record");
}
record_fields_.push_back(data_type);
}
Option::Factory*
OptionDefinition::getFactory() const {
return (OptionDefinition::factoryEmpty);
}
void
OptionDefinition::sanityCheckUniverse(const Option::Universe expected_universe,
const Option::Universe actual_universe) {
if (expected_universe != actual_universe) {
isc_throw(isc::BadValue, "invalid universe specified for the option");
}
}
void
OptionDefinition::validate() const {
if (type_ >= UNKNOWN_TYPE) {
isc_throw(isc::OutOfRange, "option type value " << type_
<< " is out of range");
}
}
OptionPtr
OptionDefinition::factoryAddrList4(Option::Universe u, uint16_t type,
const OptionBuffer& buf) {
sanityCheckUniverse(u, Option::V4);
boost::shared_ptr<Option4AddrLst> option(new Option4AddrLst(type, buf.begin(),
buf.begin() + buf.size()));
return (option);
}
OptionPtr
OptionDefinition::factoryAddrList6(Option::Universe u, uint16_t type,
const OptionBuffer& buf) {
sanityCheckUniverse(u, Option::V6);
boost::shared_ptr<Option6AddrLst> option(new Option6AddrLst(type, buf.begin(),
buf.begin() + buf.size()));
return (option);
}
OptionPtr
OptionDefinition::factoryEmpty(Option::Universe u, uint16_t type, const OptionBuffer& buf) {
if (buf.size() > 0) {
isc_throw(isc::BadValue, "input option buffer must be empty"
" when creating empty option instance");
}
OptionPtr option(new Option(u, type));
return (option);
}
} // end of isc::dhcp namespace
} // end of isc namespace
// Copyright (C) 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
// 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_DEFINITION_H_
#define OPTION_DEFINITION_H_
#include "dhcp/option6_int.h"
#include "dhcp/option.h"
namespace isc {
namespace dhcp {
/// @brief Base class representing DHCP option definitions.
///
/// This is a base class representing DHCP options definitions.
/// The definition describes the format of the option. In particular
/// it defines:
/// - option name,
/// - option code,
/// - data fields order and their types,
/// - sub options space that the particular option encapsulates.
///
/// The option type specifies the data type(s) which option conveys.
/// If it is to convey single value the option type points to the
/// data type of this value. For example, DHCPv6 option 8 comprises
/// 2-byte option code, 2-byte option length and 2-byte field that
/// carries uint16 value (http://ietf.org/rfc/rfc3315.txt).
/// In such case option type is defined as "uint16". In case when
/// the particular option has more complex structure the option type
/// may be defined as "array", "record" or even "array of records".
/// Array types should be used when the option contains multiple
/// data fields of the same type laid successively. For example,
/// DHCPv6 option 6 includes multiple fields holding uint16 codes
/// of requested DHCPv6 options (http://ietf.org/rfc/rfc3315.txt).
/// Such option can be represented with this class by setting
/// option type to "uint16" and array indicator (array_type) to true.
/// The number of elements in the array is unlimited (actually it is
/// limited by the maximal DHCPv6 option length).
/// Should 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.
/// When OptionDefinition object has been sucessfully created it
/// can be queried to return the appropriate option factory function
/// for the specified option format. There is a number of "standard"
/// factory functions that cover well known (common) formats.
/// If the particular format does not match any common format the generic
/// factory function is returned.
///
/// The following data type strings are supported:
/// - "empty" - option does not contain data fields
/// - "boolean"
/// - "int8"
/// - "int16"
/// - "int32"
/// - "uint8"
/// - "uint16"
/// - "uint32"
/// - "ipv4-address" - IPv4 Address
/// - "ipv6-address" - IPV6 Address
/// - "string"
/// - "fqdn" - fully qualified name
/// - "record" - set of data fields of different types
///
/// @todo Extend the comment to describe "generic factories".
/// @todo Extend this class to use custom namespaces.
class OptionDefinition {
public:
/// Data types of DHCP option fields.
enum DataType {
EMPTY_TYPE = 0,
BOOLEAN_TYPE = 1,
INT8_TYPE = 2,
INT16_TYPE = 3,
INT32_TYPE = 4,
UINT8_TYPE = 5,
UINT16_TYPE = 6,
UINT32_TYPE = 7,
IPV4_ADDRESS_TYPE = 8,
IPV6_ADDRESS_TYPE = 9,
STRING_TYPE = 10,
FQDN_TYPE = 11,
RECORD_TYPE = 12,
UNKNOWN_TYPE = RECORD_TYPE + 1
};
/// List of fields within the record.
typedef std::vector<DataType> RecordFieldsCollection;
/// Iterator for record data fields.
typedef std::vector<DataType>::iterator RecordFieldsCollectionIter;
private:
/// @brief Utility class for operations on DataTypes.
class DataTypeUtil {
public:
/// @brief Return sole instance of this class.
///
/// @return instance of this class.
static DataTypeUtil& instance() {
static DataTypeUtil instance;
return (instance);
}
/// @brief Convert type given as string value to option data type.
///
/// @param data_type_name data type string.
///
/// @return option data type.
DataType getDataType(const std::string& data_type_name);
private:
/// @brief Constructor.
///
/// Constructor initializes the internal data structures, e.g.
/// mapping between data type name and the corresponding enum.
DataTypeUtil();
/// Map of data types, maps name of the type to enum value.
std::map<std::string, DataType> data_types_;
};
public:
/// @brief Constructor.
///
/// @param name option name.
/// @param code option code.
/// @param type option data type as string.
/// @param array_type array indicator, if true it indicates that
/// option fields are the array.
OptionDefinition(const std::string& name,
const uint16_t code,
const std::string& type,
const bool array_type = false);
/// @brief Constructor.
///
/// @param name option name.
/// @param code option code.
/// @param type option data type.
/// @param array_type array indicator, if true it indicates that
/// option fields are the array.
OptionDefinition(const std::string& name,
const uint16_t code,
const DataType type,
const bool array_type = false);
/// @brief Adds data field to the record.
///
/// @param data_type_name name of the data type for the field.
///
/// @throw isc::InvalidOperation if option type is not set to RECORD_TYPE.
/// @throw isc::BadValue if specified invalid data type.
void addRecordField(const std::string& data_type_name);
/// @brief Adds data field to the record.
///
/// @param data_type data type for the field.
///
/// @throw isc::InvalidOperation if option type is not set to RECORD_TYPE.
/// @throw isc::BadValue if specified invalid data type.
void addRecordField(const DataType data_type);
/// @brief Return array type indicator.
///
/// The method returns the bool value to indicate wheter
/// option is single value or array of values.
///
/// @return true if option comprises an array of values.
bool getArrayType() const { return (array_type_); }
/// @brief Return option code.
///
/// @return option code.
uint16_t getCode() const { return (code_); }
/// @brief Return factory function for the given definition.
///
/// @return pointer to factory function.
Option::Factory* getFactory() const;
/// @brief Return option name.
///
/// @return option name.
const std::string& getName() const { return (name_); }
/// @brief Return list of record fields.
///
/// @return list of record fields.
const RecordFieldsCollection& getRecordFields() const { return (record_fields_); }
/// @brief Return option data type.
///
/// @return option data type.
DataType getType() const { return (type_); };
/// @brief Factory to create option with address list.
///
/// @param u universe (must be V4).
/// @param type option type.
/// @param buf option buffer with a list of IPv4 addresses.
static OptionPtr factoryAddrList4(Option::Universe u, uint16_t type,
const OptionBuffer& buf);
/// @brief Factory to create option with address list.
///
/// @param u universe (must be V6).
/// @param type option type.
/// @param buf option buffer with a list of IPv6 addresses.
static OptionPtr factoryAddrList6(Option::Universe u, uint16_t type,
const OptionBuffer& buf);
/// @brief Empty option factory.
///
/// @param u universe (V6 or V4).
/// @param type option type.
/// @param buf option buffer (must be empty).
static OptionPtr factoryEmpty(Option::Universe u, uint16_t type,
const OptionBuffer& buf);
/// @brief Factory function to create option with integer value.
///
/// @param u universe (V6 or V4).
/// @param type option type.
/// @param buf option buffer (must be empty).
/// @param T type of the data field (must be one of the uintX_t or intX_t).
template<typename T>
static OptionPtr factoryInteger(Option::Universe, uint16_t type, const OptionBuffer& buf) {
if (buf.size() > sizeof(T)) {
isc_throw(isc::OutOfRange, "provided option buffer is too large, expected: "
<< sizeof(T) << " bytes");
}
OptionPtr option(new OptionInt6<T>(type, buf.begin(), buf.begin() + buf.size()));
return (option);
}
private:
/// @brief Sanity check universe value.
///
/// @param expected_universe expected universe value.
/// @param actual_universe actual universe value.
///
/// @throw isc::BadValue if expected universe and actual universe don't match.
static inline void sanityCheckUniverse(const Option::Universe expected_universe,
const Option::Universe actual_universe);
/// @brief Check if the option definition is valid.
///
/// @todo: list exceptions it throws.
void validate() const;
/// Option name.
std::string name_;
/// Option code.
uint16_t code_;
/// Option data type.
DataType type_;
/// Indicates wheter option is a single value or array.
bool array_type_;
/// Collection of data fields within the record.
RecordFieldsCollection record_fields_;
};
} // namespace isc::dhcp
} // namespace isc
#endif
......@@ -33,6 +33,7 @@ libdhcp___unittests_SOURCES += option6_ia_unittest.cc
libdhcp___unittests_SOURCES += option6_addrlst_unittest.cc
libdhcp___unittests_SOURCES += option4_addrlst_unittest.cc
libdhcp___unittests_SOURCES += option_unittest.cc
libdhcp___unittests_SOURCES += option_definition_unittest.cc
libdhcp___unittests_SOURCES += pkt6_unittest.cc
libdhcp___unittests_SOURCES += pkt4_unittest.cc
......
// Copyright (C) 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
// 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.
#include <config.h>
#include <iostream>
#include <sstream>
#include <gtest/gtest.h>
#include <boost/shared_ptr.hpp>
#include <boost/pointer_cast.hpp>
#include <exceptions/exceptions.h>
#include <asiolink/io_address.h>
#include "dhcp/dhcp4.h"
#include "dhcp/dhcp6.h"
#include "dhcp/option4_addrlst.h"
#include "dhcp/option6_addrlst.h"
#include "dhcp/option6_int.h"
#include "dhcp/option_definition.h"
using namespace std;
using namespace isc;
using namespace isc::dhcp;
using namespace isc::util;
namespace {
class OptionDefinitionTest : public ::testing::Test {
public:
OptionDefinitionTest() { }
};