mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-30 13:37:55 +00:00
[2312] Created OptionCustom class.
This commit is contained in:
@@ -23,6 +23,7 @@ libb10_dhcp___la_SOURCES += libdhcp++.cc libdhcp++.h
|
||||
libb10_dhcp___la_SOURCES += option.cc option.h
|
||||
libb10_dhcp___la_SOURCES += option_data_types.h
|
||||
libb10_dhcp___la_SOURCES += option_definition.cc option_definition.h
|
||||
libb10_dhcp___la_SOURCES += option_custom.cc option_custom.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
|
||||
|
179
src/lib/dhcp/option_custom.cc
Normal file
179
src/lib/dhcp/option_custom.cc
Normal file
@@ -0,0 +1,179 @@
|
||||
// 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/libdhcp++.h>
|
||||
#include <dhcp/option_data_types.h>
|
||||
#include <dhcp/option_custom.h>
|
||||
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
|
||||
OptionCustom::OptionCustom(const OptionDefinition& def,
|
||||
Universe u,
|
||||
const OptionBuffer& data)
|
||||
: Option(u, def.getCode(), data.begin(), data.end()),
|
||||
definition_(def),
|
||||
init_passed_(true) {
|
||||
try {
|
||||
createBuffers();
|
||||
} catch (const Exception& ex) {
|
||||
init_passed_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
OptionCustom::OptionCustom(const OptionDefinition& def,
|
||||
Universe u,
|
||||
OptionBufferConstIter first,
|
||||
OptionBufferConstIter last)
|
||||
: Option(u, def.getCode(), first, last),
|
||||
definition_(def),
|
||||
init_passed_(true) {
|
||||
try {
|
||||
createBuffers();
|
||||
} catch (const Exception& ex) {
|
||||
init_passed_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
OptionCustom::checkIndex(const uint32_t index) const {
|
||||
if (index >= buffers_.size()) {
|
||||
isc_throw(isc::OutOfRange, "specified data field index " << index
|
||||
<< " is out of rangex.");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
OptionCustom::createBuffers() {
|
||||
// Check that the option definition is correct as we are going
|
||||
// to use it to split the data_ buffer into set of sub buffers.
|
||||
definition_.validate();
|
||||
|
||||
// @todo create buffers here
|
||||
}
|
||||
|
||||
void
|
||||
OptionCustom::pack4(isc::util::OutputBuffer& buf) {
|
||||
if (len() > 255) {
|
||||
isc_throw(OutOfRange, "DHCPv4 Option " << type_ << " is too big."
|
||||
<< " At most 255 bytes are supported.");
|
||||
}
|
||||
|
||||
buf.writeUint8(type_);
|
||||
buf.writeUint8(len() - getHeaderLen());
|
||||
|
||||
// @todo write option data here
|
||||
|
||||
LibDHCP::packOptions(buf, options_);
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
OptionCustom::pack6(isc::util::OutputBuffer& buf) {
|
||||
buf.writeUint16(type_);
|
||||
buf.writeUint16(len() - getHeaderLen());
|
||||
|
||||
// @todo write option data here.
|
||||
|
||||
LibDHCP::packOptions(buf, options_);
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
OptionCustom::readAddress(const uint32_t index, asiolink::IOAddress&) const {
|
||||
checkIndex(index);
|
||||
}
|
||||
|
||||
bool
|
||||
OptionCustom::readBoolean(const uint32_t index) const {
|
||||
checkIndex(index);
|
||||
return (true);
|
||||
}
|
||||
|
||||
void
|
||||
OptionCustom::readString(const uint32_t index, std::string&) const {
|
||||
checkIndex(index);
|
||||
}
|
||||
|
||||
void
|
||||
OptionCustom::unpack(OptionBufferConstIter begin,
|
||||
OptionBufferConstIter end) {
|
||||
data_ = OptionBuffer(begin, end);
|
||||
}
|
||||
|
||||
uint16_t
|
||||
OptionCustom::len() {
|
||||
// Returns length of the complete option (data length + DHCPv4/DHCPv6
|
||||
// option header)
|
||||
|
||||
// length of the whole option is header and data stored in this option...
|
||||
int length = getHeaderLen() + data_.size();
|
||||
|
||||
// ... and sum of lengths of all suboptions
|
||||
for (OptionCustom::OptionCollection::iterator it = options_.begin();
|
||||
it != options_.end();
|
||||
++it) {
|
||||
length += (*it).second->len();
|
||||
}
|
||||
|
||||
// note that this is not equal to lenght field. This value denotes
|
||||
// number of bytes required to store this option. length option should
|
||||
// contain (len()-getHeaderLen()) value.
|
||||
return (length);
|
||||
}
|
||||
|
||||
bool
|
||||
OptionCustom::valid() {
|
||||
if ((universe_ != V4 && universe_ != V6) ||
|
||||
!init_passed_) {
|
||||
return (false);
|
||||
}
|
||||
|
||||
return (true);
|
||||
}
|
||||
|
||||
std::string OptionCustom::toText(int /* =0 */ ) {
|
||||
std::stringstream tmp;
|
||||
|
||||
/* for (int i = 0; i < indent; i++)
|
||||
tmp << " ";
|
||||
|
||||
tmp << "type=" << type_ << ", len=" << len()-getHeaderLen() << ": ";
|
||||
|
||||
for (unsigned int i = 0; i < data_.size(); i++) {
|
||||
if (i) {
|
||||
tmp << ":";
|
||||
}
|
||||
tmp << setfill('0') << setw(2) << hex
|
||||
<< static_cast<unsigned short>(data_[i]);
|
||||
}
|
||||
|
||||
// print suboptions
|
||||
for (OptionCollection::const_iterator opt = options_.begin();
|
||||
opt != options_.end();
|
||||
++opt) {
|
||||
tmp << (*opt).second->toText(indent+2);
|
||||
} */
|
||||
return tmp.str();
|
||||
}
|
||||
|
||||
void OptionCustom::setData(const OptionBufferConstIter first,
|
||||
const OptionBufferConstIter last) {
|
||||
// We will copy entire option buffer, so we have to resize data_.
|
||||
data_.resize(std::distance(first, last));
|
||||
std::copy(first, last, data_.begin());
|
||||
}
|
||||
|
||||
} // end of isc::dhcp namespace
|
||||
} // end of isc namespace
|
201
src/lib/dhcp/option_custom.h
Normal file
201
src/lib/dhcp/option_custom.h
Normal file
@@ -0,0 +1,201 @@
|
||||
// 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_CUSTOM_H
|
||||
#define OPTION_CUSTOM_H
|
||||
|
||||
#include <dhcp/option.h>
|
||||
#include <dhcp/option_definition.h>
|
||||
#include <util/io_utilities.h>
|
||||
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
|
||||
/// @brief Option with defined data fields represented as buffers that can
|
||||
/// be accessed using data field index.
|
||||
///
|
||||
/// This class represents an option which has defined structure: data fields
|
||||
/// of specific types and order. Those fields can be accessed using indexes,
|
||||
/// where index 0 represents first data field within an option. The last
|
||||
/// field can be accessed using index equal to 'number of fields' - 1.
|
||||
/// Internally, the option data is stored as a collection of OptionBuffer
|
||||
/// objects, each representing data for a particular data field. This data
|
||||
/// can be converted to the actual data type using methods implemented
|
||||
/// within this class. This class is used to represent those options that
|
||||
/// can't be represented by any other specialized class (this excludes the
|
||||
/// Option class which is generic and can be used to represent any option).
|
||||
class OptionCustom : public Option {
|
||||
public:
|
||||
|
||||
class OptionFieldBuffer {
|
||||
public:
|
||||
OptionFieldBuffer(OptionDataType type,
|
||||
const OptionBuffer& buf)
|
||||
: type_(type), buf_(buf) {
|
||||
}
|
||||
|
||||
const OptionBuffer& getBuffer() const {
|
||||
return (buf_);
|
||||
}
|
||||
|
||||
OptionDataType getType() const {
|
||||
return (type_);
|
||||
}
|
||||
|
||||
private:
|
||||
OptionDataType type_;
|
||||
OptionBuffer buf_;
|
||||
};
|
||||
|
||||
/// @brief Constructor, used for options to be sent.
|
||||
///
|
||||
/// @param u specifies universe (V4 or V6).
|
||||
/// @param def option definition.
|
||||
/// @param data content of the option.
|
||||
OptionCustom(const OptionDefinition& def, Universe u, const OptionBuffer& data);
|
||||
|
||||
/// @brief Constructor, used for received options.
|
||||
///
|
||||
/// @param u specifies universe (V4 or V6).
|
||||
/// @param def option definition.
|
||||
/// @param first iterator to the first element that should be copied.
|
||||
/// @param last iterator to the next element after the last one
|
||||
/// to be copied.
|
||||
OptionCustom(const OptionDefinition& def, Universe u,
|
||||
OptionBufferConstIter first, OptionBufferConstIter last);
|
||||
|
||||
void readAddress(const uint32_t index, asiolink::IOAddress& address) const;
|
||||
|
||||
bool readBoolean(const uint32_t index) const;
|
||||
|
||||
template<typename T>
|
||||
T readInteger(const uint32_t index) const {
|
||||
checkIndex(index);
|
||||
|
||||
if (!OptionDataTypeTraits<T>::integer_type) {
|
||||
isc_throw(isc::dhcp::InvalidDataType, "specified data type to be returned"
|
||||
" by readInteger is not supported integer type");
|
||||
}
|
||||
|
||||
OptionDataType data_type = definition_.getType();
|
||||
if (data_type == OPT_RECORD_TYPE) {
|
||||
const OptionDefinition::RecordFieldsCollection& record_fields =
|
||||
definition_.getRecordFields();
|
||||
assert(index < record_fields.size());
|
||||
data_type = record_fields[index];
|
||||
}
|
||||
|
||||
if (OptionDataTypeTraits<T>::type != data_type) {
|
||||
isc_throw(isc::dhcp::InvalidDataType,
|
||||
"unable to read option field with index " << index
|
||||
<< " as integer value. The field's data type"
|
||||
<< data_type << " does not match the integer type"
|
||||
<< "returned by the readInteger function.");
|
||||
}
|
||||
assert(buffers_[index].size() == OptionDataTypeTraits<T>::len);
|
||||
T value;
|
||||
switch (OptionDataTypeTraits<T>::len) {
|
||||
case 1:
|
||||
value = *(buffers_[index].begin());
|
||||
break;
|
||||
case 2:
|
||||
value = isc::util::readUint16(&(*buffers_[index].begin()));
|
||||
break;
|
||||
case 4:
|
||||
value = isc::util::readUint32(&(*buffers_[index].begin()));
|
||||
break;
|
||||
default:
|
||||
// This should not happen because we made checks on data types
|
||||
// but it does not hurt to keep throw statement here.
|
||||
isc_throw(isc::dhcp::InvalidDataType,
|
||||
"invalid size of the data type to be read as integer.");
|
||||
}
|
||||
return (value);
|
||||
}
|
||||
|
||||
void readString(const uint32_t index, std::string& value) const;
|
||||
|
||||
/// @brief Parses received buffer.
|
||||
///
|
||||
/// @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);
|
||||
|
||||
/// Returns string representation of the option.
|
||||
///
|
||||
/// @param indent number of spaces before printed text.
|
||||
///
|
||||
/// @return string with text representation.
|
||||
virtual std::string toText(int indent = 0);
|
||||
|
||||
/// Returns length of the complete option (data length + DHCPv4/DHCPv6
|
||||
/// option header)
|
||||
///
|
||||
/// @return length of the option
|
||||
virtual uint16_t len();
|
||||
|
||||
/// Check if option is valid.
|
||||
///
|
||||
/// @return true, if option is valid.
|
||||
virtual bool valid();
|
||||
|
||||
/// Returns pointer to actual data.
|
||||
///
|
||||
/// @return pointer to actual data (or reference to an empty vector
|
||||
/// if there is no data).
|
||||
virtual const OptionBuffer& getData() { return (data_); }
|
||||
|
||||
/// @brief Sets content of this option from buffer.
|
||||
///
|
||||
/// Option will be resized to length of buffer.
|
||||
///
|
||||
/// @param first iterator pointing begining of buffer to copy.
|
||||
/// @param last iterator pointing to end of buffer to copy.
|
||||
void setData(const OptionBufferConstIter first,
|
||||
const OptionBufferConstIter last);
|
||||
|
||||
protected:
|
||||
|
||||
/// @brief Writes DHCPv4 option in a wire format to a buffer.
|
||||
///
|
||||
/// @param buf output buffer (option will be stored there).
|
||||
virtual void pack4(isc::util::OutputBuffer& buf);
|
||||
|
||||
/// @brief Writes DHCPv6 option in a wire format to a buffer.
|
||||
///
|
||||
/// @param buf output buffer (built options will be stored here)
|
||||
virtual void pack6(isc::util::OutputBuffer& buf);
|
||||
|
||||
private:
|
||||
|
||||
/// @brief Check if data field index is valid.
|
||||
///
|
||||
/// @throw isc::OutOfRange if index is out of range.
|
||||
void checkIndex(const uint32_t index) const;
|
||||
|
||||
/// @brief Create collection of buffers representing data field values.
|
||||
void createBuffers();
|
||||
|
||||
OptionDefinition definition_;
|
||||
|
||||
bool init_passed_;
|
||||
|
||||
std::vector<OptionBuffer> buffers_;
|
||||
};
|
||||
|
||||
} // namespace isc::dhcp
|
||||
} // namespace isc
|
||||
|
||||
#endif // OPTION_CUSTOM_H
|
@@ -36,6 +36,7 @@ libdhcp___unittests_SOURCES += option6_iaaddr_unittest.cc
|
||||
libdhcp___unittests_SOURCES += option6_int_array_unittest.cc
|
||||
libdhcp___unittests_SOURCES += option6_int_unittest.cc
|
||||
libdhcp___unittests_SOURCES += option_definition_unittest.cc
|
||||
libdhcp___unittests_SOURCES += option_custom_unittest.cc
|
||||
libdhcp___unittests_SOURCES += option_unittest.cc
|
||||
libdhcp___unittests_SOURCES += pkt4_unittest.cc
|
||||
libdhcp___unittests_SOURCES += pkt6_unittest.cc
|
||||
|
108
src/lib/dhcp/tests/option_custom_unittest.cc
Normal file
108
src/lib/dhcp/tests/option_custom_unittest.cc
Normal file
@@ -0,0 +1,108 @@
|
||||
// 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 <asiolink/io_address.h>
|
||||
#include <dhcp/option_custom.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using namespace isc;
|
||||
using namespace isc::asiolink;
|
||||
using namespace isc::dhcp;
|
||||
|
||||
namespace {
|
||||
|
||||
/// @brief OptionCustomTest test class.
|
||||
class OptionCustomTest : public ::testing::Test {
|
||||
public:
|
||||
/// @brief Constructor.
|
||||
OptionCustomTest() {
|
||||
for (int i = 0; i < 255; ++i) {
|
||||
buf_.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
void writeAddress(const asiolink::IOAddress& address,
|
||||
std::vector<uint8_t>& buf) {
|
||||
short family = address.getFamily();
|
||||
if (family == AF_INET) {
|
||||
asio::ip::address_v4::bytes_type buf_addr =
|
||||
address.getAddress().to_v4().to_bytes();
|
||||
buf.insert(buf.end(), buf_addr.begin(), buf_addr.end());
|
||||
} else if (family == AF_INET6) {
|
||||
asio::ip::address_v6::bytes_type buf_addr =
|
||||
address.getAddress().to_v6().to_bytes();
|
||||
buf.insert(buf.end(), buf_addr.begin(), buf_addr.end());
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void writeInt(T value, std::vector<uint8_t>& buf) {
|
||||
for (int i = 0; i < sizeof(T); ++i) {
|
||||
buf.push_back(value >> ((3 - i) * 8) & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
void writeString(const std::string& value,
|
||||
std::vector<uint8_t>& buf) {
|
||||
buf.resize(buf.size() + value.size());
|
||||
std::copy_backward(value.c_str(), value.c_str() + value.size(),
|
||||
buf.end());
|
||||
}
|
||||
|
||||
OptionBuffer buf_;
|
||||
};
|
||||
|
||||
TEST_F(OptionCustomTest, constructor) {
|
||||
/* OptionDefinition opt_def1("OPTION_FOO", 1000, "string", true);
|
||||
ASSERT_THROW(opt_def1.validate(), isc::Exception); */
|
||||
}
|
||||
|
||||
TEST_F(OptionCustomTest, setData) {
|
||||
OptionDefinition opt_def("OPTION_FOO", 1000, "record");
|
||||
ASSERT_NO_THROW(opt_def.addRecordField("uint16"));
|
||||
ASSERT_NO_THROW(opt_def.addRecordField("boolean"));
|
||||
ASSERT_NO_THROW(opt_def.addRecordField("ipv4-address"));
|
||||
ASSERT_NO_THROW(opt_def.addRecordField("ipv6-address"));
|
||||
ASSERT_NO_THROW(opt_def.addRecordField("string"));
|
||||
|
||||
OptionBuffer buf;
|
||||
writeInt<uint16_t>(8712, buf);
|
||||
buf.push_back(1);
|
||||
writeAddress(IOAddress("192.168.0.1"), buf);
|
||||
writeAddress(IOAddress("2001:db8:1::1"), buf);
|
||||
writeString("ABCD", buf);
|
||||
|
||||
OptionCustom option(opt_def, Option::V6, buf_.begin(), buf_.begin() + 30);
|
||||
ASSERT_TRUE(option.valid());
|
||||
|
||||
uint16_t value0 = 0;
|
||||
ASSERT_NO_THROW(value0 = option.readInteger<uint16_t>(0));
|
||||
EXPECT_EQ(0x0001, value0);
|
||||
bool value1 = false;
|
||||
ASSERT_NO_THROW(value1 = option.readBoolean(1));
|
||||
EXPECT_TRUE(value1);
|
||||
IOAddress value2("127.0.0.1");
|
||||
ASSERT_NO_THROW(option.readAddress(2, value2));
|
||||
EXPECT_EQ("192.168.0.1", value2.toText());
|
||||
IOAddress value3("::1");
|
||||
ASSERT_NO_THROW(option.readAddress(3, value3));
|
||||
EXPECT_EQ("2001:db8:1::1", value3.toText());
|
||||
std::string value4;
|
||||
ASSERT_NO_THROW(option.readString(4, value4));
|
||||
EXPECT_EQ("ABCD", value4);
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
Reference in New Issue
Block a user