From: Marcin Siodelski Date: Fri, 5 Oct 2012 18:42:20 +0000 (+0200) Subject: [2304] Added Option6IntArray option type and unit tests. X-Git-Tag: trac2402_base~5^2~10 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=31c8d5e0b4cdec573baf77f09fca75a362f641b6;p=thirdparty%2Fkea.git [2304] Added Option6IntArray option type and unit tests. --- diff --git a/src/lib/dhcp/Makefile.am b/src/lib/dhcp/Makefile.am index ef887d5508..47d482cb38 100644 --- a/src/lib/dhcp/Makefile.am +++ b/src/lib/dhcp/Makefile.am @@ -28,6 +28,7 @@ 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 += dhcp6.h dhcp4.h libb10_dhcp___la_SOURCES += pkt6.cc pkt6.h libb10_dhcp___la_SOURCES += pkt4.cc pkt4.h diff --git a/src/lib/dhcp/option6_int.h b/src/lib/dhcp/option6_int.h index 6531499de3..1ecf3dcaf1 100644 --- a/src/lib/dhcp/option6_int.h +++ b/src/lib/dhcp/option6_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 OPTION_INT6_H_ -#define OPTION_INT6_H_ +#ifndef OPTION6_INT_H_ +#define OPTION6_INT_H_ #include #include @@ -144,10 +144,10 @@ public: private: - T value_; + T value_; ///< Value cabveyed by the option. }; } // isc::dhcp namespace } // isc namespace -#endif /* OPTION_IA_H_ */ +#endif /* OPTION6_INT_H_ */ diff --git a/src/lib/dhcp/option6_int_array.h b/src/lib/dhcp/option6_int_array.h new file mode 100644 index 0000000000..6783bf3808 --- /dev/null +++ b/src/lib/dhcp/option6_int_array.h @@ -0,0 +1,164 @@ +// 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 OPTION6_INT_ARRAY_H_ +#define OPTION6_INT_ARRAY_H_ + +#include +#include +#include +#include "dhcp/libdhcp++.h" +#include "dhcp/option.h" +#include "dhcp/option_data_types.h" + +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: +/// - uint8_t, +/// - uint16_t, +/// - uint32_t, +/// - int8_t, +/// - int16_t, +/// - int32_t. +/// +/// @param T data field type (see above). +template +class Option6IntArray: public Option { + +public: + + typedef std::vector ValuesCollection; + + /// @brief Constructor. + /// + /// @param type option type. + /// @param value option value. + Option6IntArray(uint16_t type, const ValuesCollection& values) + : Option(Option::V6, type), values_(values) { + if (!OptionDataTypes::valid) { + isc_throw(dhcp::InvalidDataType, "non-numeric type"); + } + } + + /// @brief Constructor. + /// + /// This constructor creates option from a buffer. This construtor + /// may throw exception if \ref unpack function throws during buffer + /// parsing. + /// + /// @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). + /// + /// @todo mention here what it throws. + Option6IntArray(uint16_t type, OptionBufferConstIter begin, + OptionBufferConstIter end) + : Option(Option::V6, type) { + if (!OptionDataTypes::valid) { + isc_throw(dhcp::InvalidDataType, "non-numeric type"); + } + unpack(begin, end); + } + + /// Writes option in wire-format to buf, returns pointer to first unused + /// byte after stored option. + /// + /// @param [out] buf buffer (option will be stored here) + /// + /// @throw isc::BadValue if invalid option type has been provided. + void pack(isc::util::OutputBuffer& buf) { + buf.writeUint16(type_); + buf.writeUint16(len() - OPTION6_HDR_LEN); + for (int i = 0; i < values_.size(); ++i) { + switch (OptionDataTypes::len) { + case 1: + buf.writeUint8(values_[i]); + break; + case 2: + buf.writeUint16(values_[i]); + break; + case 4: + buf.writeUint32(values_[i]); + break; + default: + isc_throw(dhcp::InvalidDataType, "non-numeric type"); + } + } + } + + /// @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) != 0) { + isc_throw(OutOfRange, "Option " << type_ << " truncated"); + } + values_.clear(); + while (begin != end) { + switch (OptionDataTypes::len) { + case 1: + values_.push_back(*begin); + break; + case 2: + values_.push_back(isc::util::readUint16(&(*begin))); + break; + case 4: + values_.push_back(isc::util::readUint32(&(*begin))); + break; + default: + isc_throw(dhcp::InvalidDataType, "non-numeric type"); + } + begin += sizeof(T); + } + + LibDHCP::unpackOptions6(OptionBuffer(begin, end), options_); + } + + /// @brief Returns collection of values. + /// + /// @return collection of values. + const ValuesCollection& getValues() const { return values_; } + + /// @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 + values_.size() * sizeof(T); + // length of all suboptions + for (Option::OptionCollection::iterator it = options_.begin(); + it != options_.end(); + ++it) { + length += (*it).second->len(); + } + return (length); + } + +private: + + ValuesCollection values_; +}; + +} // isc::dhcp namespace +} // isc namespace + +#endif /* OPTION6_INT_ARRAY_H_ */ diff --git a/src/lib/dhcp/option_data_types.h b/src/lib/dhcp/option_data_types.h index d23ae9b9a1..52be59f97a 100644 --- a/src/lib/dhcp/option_data_types.h +++ b/src/lib/dhcp/option_data_types.h @@ -15,6 +15,7 @@ #ifndef OPTION_DATA_TYPES_H_ #define OPTION_DATA_TYPES_H_ +#include #include namespace isc { diff --git a/src/lib/dhcp/option_definition.cc b/src/lib/dhcp/option_definition.cc index fef7176276..63f3758ae3 100644 --- a/src/lib/dhcp/option_definition.cc +++ b/src/lib/dhcp/option_definition.cc @@ -18,6 +18,7 @@ #include "dhcp/option6_ia.h" #include "dhcp/option6_iaaddr.h" #include "dhcp/option6_int.h" +#include "dhcp/option6_int_array.h" using namespace std; using namespace isc::util; diff --git a/src/lib/dhcp/option_definition.h b/src/lib/dhcp/option_definition.h index 73df8604a5..ade275a8df 100644 --- a/src/lib/dhcp/option_definition.h +++ b/src/lib/dhcp/option_definition.h @@ -15,7 +15,9 @@ #ifndef OPTION_DEFINITION_H_ #define OPTION_DEFINITION_H_ +#include "dhcp/option_data_types.h" #include "dhcp/option6_int.h" +#include "dhcp/option6_int_array.h" #include "dhcp/option.h" namespace isc { @@ -258,7 +260,7 @@ public: /// /// @param u universe (V6 or V4). /// @param type option type. - /// @param buf option buffer (must be empty). + /// @param buf option buffer. /// @param T type of the data field (must be one of the uintX_t or intX_t). template static OptionPtr factoryInteger(Option::Universe, uint16_t type, const OptionBuffer& buf) { @@ -266,7 +268,25 @@ public: isc_throw(isc::OutOfRange, "provided option buffer is too large, expected: " << sizeof(T) << " bytes"); } - OptionPtr option(new Option6Int(type, buf.begin(), buf.begin() + buf.size())); + OptionPtr option(new Option6Int(type, buf.begin(), buf.end())); + return (option); + } + + /// @brief Factory function to create option with array of integer values. + /// + /// @param u universe (V6 or V4). + /// @param type option type. + /// @param buf option buffer. + /// @param T type of the data field (must be one of the uintX_t or intX_t). + template + static OptionPtr factoryIntegerArray(Option::Universe, uint16_t type, const OptionBuffer& buf) { + if (buf.size() == 0) { + isc_throw(isc::OutOfRange, "option buffer length must be greater than zero"); + } else if (buf.size() % OptionDataTypes::len != 0) { + isc_throw(isc::OutOfRange, "option buffer length must be multiple of " + << OptionDataTypes::len << " bytes"); + } + OptionPtr option(new Option6IntArray(type, buf.begin(), buf.end())); return (option); } diff --git a/src/lib/dhcp/tests/option_definition_unittest.cc b/src/lib/dhcp/tests/option_definition_unittest.cc index 05536b09ac..d1a7a96619 100644 --- a/src/lib/dhcp/tests/option_definition_unittest.cc +++ b/src/lib/dhcp/tests/option_definition_unittest.cc @@ -29,6 +29,7 @@ #include "dhcp/option6_ia.h" #include "dhcp/option6_iaaddr.h" #include "dhcp/option6_int.h" +#include "dhcp/option6_int_array.h" #include "dhcp/option_definition.h" using namespace std; @@ -319,7 +320,7 @@ TEST_F(OptionDefinitionTest, factoryIAAddr6) { EXPECT_EQ(0x04050607, option_cast_v6->getValid()); } -TEST_F(OptionDefinitionTest, factoryIntegerNegative) { +TEST_F(OptionDefinitionTest, factoryIntegerInvalidType) { EXPECT_THROW( OptionDefinition::factoryInteger(Option::V6, D6O_PREFERENCE, OptionBuffer(1)), isc::dhcp::InvalidDataType @@ -408,4 +409,84 @@ TEST_F(OptionDefinitionTest, factoryInteger32) { // @todo Add more cases for DHCPv4 } +TEST_F(OptionDefinitionTest, factoryInteger16Array) { + Option::Factory* f = OptionDefinition::factoryIntegerArray; + OptionPtr option_v6; + // Provided buffer size must be greater than zero. Check if we + // get exception if we provide zero-length buffer. + EXPECT_THROW( + option_v6 = f(Option::V6, 79, OptionBuffer()), + isc::OutOfRange + ); + // Buffer length must be multiple of data type size. + EXPECT_THROW( + option_v6 = f(Option::V6, 79, OptionBuffer(5)), + isc::OutOfRange + ); + // Positive scenario, initiate the buffer with length being + // multiple of uint16_t size. + // buffer elements will be: 0x112233. + OptionBuffer buf(6); + for (int i = 0; i < 6; ++i) { + buf[i] = i / 2; + } + // Constructor should succeed because buffer has correct size. + EXPECT_NO_THROW( + option_v6 = f(Option::V6, 79, buf); + ); + boost::shared_ptr > option_cast_v6 = + boost::static_pointer_cast >(option_v6); + // Get the values from the initiated options and validate. + Option6IntArray::ValuesCollection values = + option_cast_v6->getValues(); + for (int i = 0; i < values.size(); ++i) { + // Expected value is calculated using on the same pattern + // as the one we used to initiate buffer: + // for i=0, expected = 0x00, for i = 1, expected == 0x11 etc. + uint16_t expected = (i << 8) | i; + EXPECT_EQ(expected, values[i]); + } +} + +TEST_F(OptionDefinitionTest, factoryInteger32Array) { + Option::Factory* f = OptionDefinition::factoryIntegerArray; + const uint16_t opt_code = 80; + OptionPtr option_v6; + // Provided buffer size must be greater than zero. Check if we + // get exception if we provide zero-length buffer. + EXPECT_THROW( + option_v6 = f(Option::V6, opt_code, OptionBuffer()), + isc::OutOfRange + ); + // Buffer length must be multiple of data type size. + EXPECT_THROW( + option_v6 = f(Option::V6, opt_code, OptionBuffer(5)), + isc::OutOfRange + ); + // Positive scenario, initiate the buffer with length being + // multiple of uint16_t size. + // buffer elements will be: 0x111122223333. + OptionBuffer buf(12); + for (int i = 0; i < buf.size(); ++i) { + buf[i] = i / 4; + } + // Constructor should succeed because buffer has correct size. + EXPECT_NO_THROW( + option_v6 = f(Option::V6, opt_code, buf); + ); + boost::shared_ptr > option_cast_v6 = + boost::static_pointer_cast >(option_v6); + // Get the values from the initiated options and validate. + Option6IntArray::ValuesCollection values = + option_cast_v6->getValues(); + for (int i = 0; i < values.size(); ++i) { + // Expected value is calculated using on the same pattern + // as the one we used to initiate buffer: + // for i=0, expected = 0x0000, for i = 1, expected == 0x1111 etc. + uint32_t expected = 0x01010101 * i; + EXPECT_EQ(expected, values[i]); + } +} + + } // anonymous namespace