Commit 50a73567 authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[master] Merge branch 'trac2526'

Conflicts:
	src/lib/dhcp/pkt4.cc
	src/lib/dhcp/std_option_defs.h
parents 01496b7e 030ab7aa
......@@ -23,8 +23,8 @@
#include <dhcp/option6_ia.h>
#include <dhcp/option6_iaaddr.h>
#include <dhcp/option6_iaaddr.h>
#include <dhcp/option6_int_array.h>
#include <dhcp/option_custom.h>
#include <dhcp/option_int_array.h>
#include <dhcp/pkt6.h>
#include <dhcp6/dhcp6_log.h>
#include <dhcp6/dhcp6_srv.h>
......@@ -331,8 +331,8 @@ void Dhcpv6Srv::appendRequestedOptions(const Pkt6Ptr& question, Pkt6Ptr& answer)
// Client requests some options using ORO option. Try to
// get this option from client's message.
boost::shared_ptr<Option6IntArray<uint16_t> > option_oro =
boost::dynamic_pointer_cast<Option6IntArray<uint16_t> >(question->getOption(D6O_ORO));
boost::shared_ptr<OptionIntArray<uint16_t> > option_oro =
boost::dynamic_pointer_cast<OptionIntArray<uint16_t> >(question->getOption(D6O_ORO));
// Option ORO not found. Don't do anything then.
if (!option_oro) {
return;
......
......@@ -23,7 +23,7 @@
#include <dhcp/option6_addrlst.h>
#include <dhcp/option6_ia.h>
#include <dhcp/option6_iaaddr.h>
#include <dhcp/option6_int_array.h>
#include <dhcp/option_int_array.h>
#include <dhcp6/config_parser.h>
#include <dhcp6/dhcp6_srv.h>
#include <dhcpsrv/cfgmgr.h>
......@@ -381,8 +381,8 @@ TEST_F(Dhcpv6SrvTest, advertiseOptions) {
// Let's now request option with code 1000.
// We expect that server will include this option in its reply.
boost::shared_ptr<Option6IntArray<uint16_t> >
option_oro(new Option6IntArray<uint16_t>(D6O_ORO));
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;
......
......@@ -28,8 +28,8 @@ 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 += option6_int_array.h
libb10_dhcp___la_SOURCES += option_int.h
libb10_dhcp___la_SOURCES += option_int_array.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,8 +20,8 @@
#include <dhcp/option.h>
#include <dhcp/option6_ia.h>
#include <dhcp/option6_iaaddr.h>
#include <dhcp/option6_int_array.h>
#include <dhcp/option_definition.h>
#include <dhcp/option_int_array.h>
#include <dhcp/std_option_defs.h>
#include <exceptions/exceptions.h>
#include <util/buffer.h>
......@@ -49,10 +49,12 @@ const OptionDefContainer&
LibDHCP::getOptionDefs(const Option::Universe u) {
switch (u) {
case Option::V4:
initStdOptionDefs4();
if (v4option_defs_.empty()) {
initStdOptionDefs4();
}
return (v4option_defs_);
case Option::V6:
if (v6option_defs_.size() == 0) {
if (v6option_defs_.empty()) {
initStdOptionDefs6();
}
return (v6option_defs_);
......@@ -100,31 +102,35 @@ LibDHCP::optionFactory(Option::Universe u,
size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
isc::dhcp::Option::OptionCollection& options) {
size_t offset = 0;
size_t end = buf.size();
while (offset +4 <= end) {
uint16_t opt_type = buf[offset] * 256 + buf[offset + 1];
size_t length = buf.size();
// Get the list of stdandard option definitions.
const 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>();
// The buffer being read comprises a set of options, each starting with
// a two-byte type code and a two-byte length field.
while (offset + 4 <= length) {
uint16_t opt_type = isc::util::readUint16(&buf[offset]);
offset += 2;
uint16_t opt_len = buf[offset] * 256 + buf[offset + 1];
uint16_t opt_len = isc::util::readUint16(&buf[offset]);
offset += 2;
if (offset + opt_len > end) {
if (offset + opt_len > length) {
// @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.
// Get all definitions 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;
if (num_defs > 1) {
// Multiple options of the same code are not supported right now!
......@@ -162,7 +168,14 @@ size_t LibDHCP::unpackOptions4(const OptionBuffer& buf,
isc::dhcp::Option::OptionCollection& options) {
size_t offset = 0;
// 2 byte - header of DHCPv4 option
// Get the list of stdandard option definitions.
const OptionDefContainer& option_defs = LibDHCP::getOptionDefs(Option::V4);
// Get the search index #1. It allows to search for option definitions
// using option code.
const OptionDefContainerTypeIndex& idx = option_defs.get<1>();
// The buffer being read comprises a set of options, each starting with
// a one-byte type code and a one-byte length field.
while (offset + 1 <= buf.size()) {
uint8_t opt_type = buf[offset++];
......@@ -176,8 +189,10 @@ size_t LibDHCP::unpackOptions4(const OptionBuffer& buf,
continue;
if (offset + 1 >= buf.size()) {
// opt_type must be cast to integer so as it is not treated as
// unsigned char value (a number is presented in error message).
isc_throw(OutOfRange, "Attempt to parse truncated option "
<< opt_type);
<< static_cast<int>(opt_type));
}
uint8_t opt_len = buf[offset++];
......@@ -187,12 +202,35 @@ size_t LibDHCP::unpackOptions4(const OptionBuffer& buf,
<< "-byte long buffer.");
}
// Get all definitions 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) {
default:
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 " << static_cast<int>(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) {
opt = OptionPtr(new Option(Option::V4, opt_type,
buf.begin()+offset,
buf.begin()+offset+opt_len));
buf.begin() + offset,
buf.begin() + offset + opt_len));
} 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::V4, opt_type,
buf.begin() + offset,
buf.begin() + offset + opt_len);
}
options.insert(std::make_pair(opt_type, opt));
......@@ -259,7 +297,32 @@ void LibDHCP::OptionFactoryRegister(Option::Universe u,
void
LibDHCP::initStdOptionDefs4() {
isc_throw(isc::NotImplemented, "initStdOptionDefs4 is not implemented");
v4option_defs_.clear();
// Now let's add all option definitions.
for (int i = 0; i < OPTION_DEF_PARAMS_SIZE4; ++i) {
OptionDefinitionPtr definition(new OptionDefinition(OPTION_DEF_PARAMS4[i].name,
OPTION_DEF_PARAMS4[i].code,
OPTION_DEF_PARAMS4[i].type,
OPTION_DEF_PARAMS4[i].array));
for (int rec = 0; rec < OPTION_DEF_PARAMS4[i].records_size; ++rec) {
definition->addRecordField(OPTION_DEF_PARAMS4[i].records[rec]);
}
// Sanity check if the option is valid.
try {
definition->validate();
} catch (const Exception& ex) {
// 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.
v4option_defs_.clear();
throw;
}
v4option_defs_.push_back(definition);
}
}
void
......
......@@ -138,9 +138,9 @@ private:
/// The method creates option definitions for all DHCPv4 options.
/// Currently this function is not implemented.
///
/// @todo implemend this function.
///
/// @throw isc::NotImplemeneted
/// @throw std::bad alloc if system went out of memory.
/// @throw MalformedOptionDefinition if any of the definitions
/// are incorrect. This is programming error.
static void initStdOptionDefs4();
/// Initialize standard DHCPv6 option definitions.
......@@ -149,7 +149,7 @@ private:
///
/// @throw std::bad_alloc if system went out of memory.
/// @throw MalformedOptionDefinition if any of the definitions
/// is incorect. This is a programming error.
/// is incorrect. This is a programming error.
static void initStdOptionDefs6();
/// pointers to factories that produce DHCPv6 options
......
......@@ -100,22 +100,14 @@ void Option::pack(isc::util::OutputBuffer& buf) {
void
Option::pack4(isc::util::OutputBuffer& buf) {
if (universe_ == V4) {
if (len() > 255) {
isc_throw(OutOfRange, "DHCPv4 Option " << type_ << " is too big. "
<< "At most 255 bytes are supported.");
/// TODO Larger options can be stored as separate instances
/// of DHCPv4 options. Clients MUST concatenate them.
/// Fortunately, there are no such large options used today.
}
buf.writeUint8(type_);
buf.writeUint8(len() - getHeaderLen());
// Write a header.
packHeader(buf);
// Write data.
if (!data_.empty()) {
buf.writeData(&data_[0], data_.size());
}
// Write sub-options.
packOptions(buf);
} else {
isc_throw(BadValue, "Invalid universe type " << universe_);
}
......@@ -125,12 +117,13 @@ Option::pack4(isc::util::OutputBuffer& buf) {
void Option::pack6(isc::util::OutputBuffer& buf) {
if (universe_ == V6) {
buf.writeUint16(type_);
buf.writeUint16(len() - getHeaderLen());
// Write a header.
packHeader(buf);
// Write data.
if (!data_.empty()) {
buf.writeData(&data_[0], data_.size());
}
// Write sub-options.
packOptions(buf);
} else {
isc_throw(BadValue, "Invalid universe type " << universe_);
......@@ -138,6 +131,26 @@ void Option::pack6(isc::util::OutputBuffer& buf) {
return;
}
void
Option::packHeader(isc::util::OutputBuffer& buf) {
if (universe_ == V4) {
if (len() > 255) {
isc_throw(OutOfRange, "DHCPv4 Option " << type_ << " is too big. "
<< "At most 255 bytes are supported.");
/// TODO Larger options can be stored as separate instances
/// of DHCPv4 options. Clients MUST concatenate them.
/// Fortunately, there are no such large options used today.
}
buf.writeUint8(type_);
buf.writeUint8(len() - getHeaderLen());
} else {
buf.writeUint16(type_);
buf.writeUint16(len() - getHeaderLen());
}
}
void
Option::packOptions(isc::util::OutputBuffer& buf) {
switch (universe_) {
......
......@@ -325,6 +325,22 @@ protected:
/// @throw BadValue Universe is not V6.
virtual void pack6(isc::util::OutputBuffer& buf);
/// @brief Store option's header in a buffer.
///
/// This method writes option's header into a buffer in the
/// on-wire format. The universe set for the particular option
/// is used to determine whether option code and length are
/// stored as 2-byte (for DHCPv6) or single-byte (for DHCPv4)
/// values. For DHCPv4 options, this method checks if the
/// length does not exceed 255 bytes and throws exception if
/// it does.
/// This method is used by derived classes to pack option's
/// header into a buffer. This method should not be called
/// directly by other classes.
///
/// @param [out] buf output buffer.
void packHeader(isc::util::OutputBuffer& buf);
/// @brief Store sub options in a buffer.
///
/// This method stores all sub-options defined for a particular
......
......@@ -70,7 +70,7 @@ enum OptionDataType {
/// @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
/// is supported by classes like OptionInt, OptionIntArray and some template
/// factory functions in OptionDefinition class.
template<typename T>
struct OptionDataTypeTraits {
......
......@@ -17,10 +17,10 @@
#include <dhcp/option6_addrlst.h>
#include <dhcp/option6_ia.h>
#include <dhcp/option6_iaaddr.h>
#include <dhcp/option6_int.h>
#include <dhcp/option6_int_array.h>
#include <dhcp/option_custom.h>
#include <dhcp/option_definition.h>
#include <dhcp/option_int.h>
#include <dhcp/option_int_array.h>
#include <util/encode/hex.h>
using namespace std;
......@@ -97,19 +97,19 @@ OptionDefinition::optionFactory(Option::Universe u, uint16_t type,
factoryInteger<int8_t>(u, type, begin, end));
case OPT_UINT16_TYPE:
return (array_type_ ? factoryIntegerArray<uint16_t>(type, begin, end) :
return (array_type_ ? factoryIntegerArray<uint16_t>(u, type, begin, end) :
factoryInteger<uint16_t>(u, type, begin, end));
case OPT_INT16_TYPE:
return (array_type_ ? factoryIntegerArray<uint16_t>(type, begin, end) :
return (array_type_ ? factoryIntegerArray<uint16_t>(u, type, begin, end) :
factoryInteger<int16_t>(u, type, begin, end));
case OPT_UINT32_TYPE:
return (array_type_ ? factoryIntegerArray<uint32_t>(type, begin, end) :
return (array_type_ ? factoryIntegerArray<uint32_t>(u, type, begin, end) :
factoryInteger<uint32_t>(u, type, begin, end));
case OPT_INT32_TYPE:
return (array_type_ ? factoryIntegerArray<uint32_t>(type, begin, end) :
return (array_type_ ? factoryIntegerArray<uint32_t>(u, type, begin, end) :
factoryInteger<int32_t>(u, type, begin, end));
case OPT_IPV4_ADDRESS_TYPE:
......
......@@ -48,25 +48,25 @@ class OptionDefinition;
/// @brief Pointer to option definition object.
typedef boost::shared_ptr<OptionDefinition> OptionDefinitionPtr;
/// @brief Forward declaration to Option6Int.
/// @brief Forward declaration to OptionInt.
///
/// This forward declaration is needed to access Option6Int class
/// without having to include option6_int.h header. This is because
/// this header includes libdhcp++.h and this causes circular
/// inclusion between libdhcp++.h, option_definition.h and
/// This forward declaration is needed to access the OptionInt class without
/// having to include the option_int.h header file. It is required because
/// this header includes libdhcp++.h, and including option_int.h would cause
/// circular inclusion between libdhcp++.h, option_definition.h and
/// option6_int.h.
template<typename T>
class Option6Int;
class OptionInt;
/// @brief Forward declaration to Option6IntArray.
/// @brief Forward declaration to OptionIntArray.
///
/// This forward declaration is needed to access Option6IntArray class
/// without having to include option6_int_array.h header. This is because
/// this header includes libdhcp++.h and this causes circular
/// inclusion between libdhcp++.h, option_definition.h and
/// option6_int_array.h.
/// This forward declaration is needed to access the OptionIntArray class
/// without having to include the option_int_array.h header file. It is
/// required because this header includes libdhcp++.h, and including
/// option_int_array.h would cause circular inclusion between libdhcp++.h,
/// option_definition.h and option_int_array.h.
template<typename T>
class Option6IntArray;
class OptionIntArray;
/// @brief Base class representing a DHCP option definition.
///
......@@ -344,6 +344,7 @@ public:
/// @brief Factory function to create option with integer value.
///
/// @param u universe (V4 or V6).
/// @param type option type.
/// @param begin iterator pointing to the beginning of the buffer.
/// @param end iterator pointing to the end of the buffer.
......@@ -351,15 +352,16 @@ public:
///
/// @throw isc::OutOfRange if provided option buffer length is invalid.
template<typename T>
static OptionPtr factoryInteger(Option::Universe, uint16_t type,
static OptionPtr factoryInteger(Option::Universe u, uint16_t type,
OptionBufferConstIter begin,
OptionBufferConstIter end) {
OptionPtr option(new Option6Int<T>(type, begin, end));
OptionPtr option(new OptionInt<T>(u, type, begin, end));
return (option);
}
/// @brief Factory function to create option with array of integer values.
///
/// @param universe (V4 or V6).
/// @param type option type.
/// @param begin iterator pointing to the beginning of the buffer.
/// @param end iterator pointing to the end of the buffer.
......@@ -367,10 +369,11 @@ public:
///
/// @throw isc::OutOfRange if provided option buffer length is invalid.
template<typename T>
static OptionPtr factoryIntegerArray(uint16_t type,
static OptionPtr factoryIntegerArray(Option::Universe u,
uint16_t type,
OptionBufferConstIter begin,
OptionBufferConstIter end) {
OptionPtr option(new Option6IntArray<T>(type, begin, end));
OptionPtr option(new OptionIntArray<T>(u, type, begin, end));
return (option);
}
......
......@@ -12,8 +12,8 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#ifndef OPTION6_INT_H
#define OPTION6_INT_H
#ifndef OPTION_INT_H
#define OPTION_INT_H
#include <dhcp/libdhcp++.h>
#include <dhcp/option.h>
......@@ -25,7 +25,7 @@
namespace isc {
namespace dhcp {
/// This template class represents DHCPv6 option with single value.
/// This template class represents DHCP option with single value.
/// This value is of integer type and can be any of the following:
/// - uint8_t,
/// - uint16_t,
......@@ -36,18 +36,19 @@ namespace dhcp {
///
/// @param T data field type (see above).
template<typename T>
class Option6Int: public Option {
class OptionInt: public Option {
public:
/// @brief Constructor.
///
/// @param u universe (V4 or V6)
/// @param type option type.
/// @param value option value.
///
/// @throw isc::dhcp::InvalidDataType if data field type provided
/// as template parameter is not a supported integer type.
Option6Int(uint16_t type, T value)
: Option(Option::V6, type), value_(value) {
OptionInt(Option::Universe u, uint16_t type, T value)
: Option(u, type), value_(value) {
if (!OptionDataTypeTraits<T>::integer_type) {
isc_throw(dhcp::InvalidDataType, "non-integer type");
}
......@@ -59,6 +60,7 @@ public:
/// may throw exception if \ref unpack function throws during buffer
/// parsing.
///
/// @param u universe (V4 or V6)
/// @param type option type.
/// @param begin iterator to first byte of option data.
/// @param end iterator to end of option data (first byte after option end).
......@@ -66,9 +68,9 @@ public:
/// @throw isc::OutOfRange if provided buffer is shorter than data size.
/// @throw isc::dhcp::InvalidDataType if data field type provided
/// as template parameter is not a supported integer type.
Option6Int(uint16_t type, OptionBufferConstIter begin,
OptionInt(Option::Universe u, uint16_t type, OptionBufferConstIter begin,
OptionBufferConstIter end)
: Option(Option::V6, type) {
: Option(u, type) {
if (!OptionDataTypeTraits<T>::integer_type) {
isc_throw(dhcp::InvalidDataType, "non-integer type");
}
......@@ -84,8 +86,8 @@ public:
/// equal to 1, 2 or 4 bytes. The data type is not checked in this function
/// because it is checked in a constructor.
void pack(isc::util::OutputBuffer& buf) {
buf.writeUint16(type_);
buf.writeUint16(len() - OPTION6_HDR_LEN);
// Pack option header.
packHeader(buf);
// Depending on the data type length we use different utility functions
// writeUint16 or writeUint32 which write the data in the network byte
// order to the provided buffer. The same functions can be safely used
......@@ -168,7 +170,10 @@ public:
///
/// @return length of this option
virtual uint16_t len() {
uint16_t length = OPTION6_HDR_LEN + sizeof(T);
// Calculate the length of the header.
uint16_t length = (getUniverse() == Option::V4) ? OPTION4_HDR_LEN : OPTION6_HDR_LEN;
// The data length is equal to size of T.
length += sizeof(T);;
// length of all suboptions
for (Option::OptionCollection::iterator it = options_.begin();
it != options_.end();
......@@ -186,4 +191,4 @@ private:
} // isc::dhcp namespace
} // isc namespace
#endif // OPTION6_INT_H
#endif // OPTION_INT_H
......@@ -12,8 +12,8 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#ifndef OPTION6_INT_ARRAY_H
#define OPTION6_INT_ARRAY_H
#ifndef OPTION_INT_ARRAY_H
#define OPTION_INT_ARRAY_H
#include <dhcp/libdhcp++.h>
#include <dhcp/option.h>
......@@ -25,9 +25,9 @@
namespace isc {
namespace dhcp {
/// This template class represents DHCPv6 option with array of
/// integer values. The type of the elements in the array can be
/// any of the following:
/// This template class represents DHCP (v4 or v6) option with an
/// array of integer values. The type of the elements in the array
/// can be any of the following:
/// - uint8_t,
/// - uint16_t,
/// - uint32_t,
......@@ -43,7 +43,7 @@ namespace dhcp {
///
/// @param T data field type (see above).
template<typename T>
class Option6IntArray: public Option {
class OptionIntArray: public Option {
public:
......@@ -51,12 +51,13 @@ public:
///
/// Creates option with empty values vector.
///
/// @param u universe (V4 or V6).
/// @param type option type.
///
/// @throw isc::dhcp::InvalidDataType if data field type provided
/// as template parameter is not a supported integer type.
Option6IntArray(uint16_t type)
: Option(Option::V6, type),
OptionIntArray(const Option::Universe u, const uint16_t type)
: Option(u, type),
values_(0) {
if (!OptionDataTypeTraits<T>::integer_type) {
isc_throw(dhcp::InvalidDataType, "non-integer type");
......@@ -65,6 +66,7 @@ public:
/// @brief Constructor.
///
/// @param u universe (V4 or V6).
/// @param type option type.
/// @param buf buffer with option data (must not be empty).
///
......@@ -72,8 +74,9 @@ public:
/// is not multiple of size of the data type in bytes.