Browse Source

[2304] Added Option6IntArray option type and unit tests.

Marcin Siodelski 12 years ago
parent
commit
31c8d5e0b4

+ 1 - 0
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

+ 4 - 4
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 <stdint.h>
 #include <limits>
@@ -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_ */

+ 164 - 0
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 <stdint.h>
+#include <limits>
+#include <util/io_utilities.h>
+#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<typename T>
+class Option6IntArray: public Option {
+
+public:
+
+    typedef std::vector<T> 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<T>::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<T>::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<T>::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<T>::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_ */

+ 1 - 0
src/lib/dhcp/option_data_types.h

@@ -15,6 +15,7 @@
 #ifndef OPTION_DATA_TYPES_H_
 #define OPTION_DATA_TYPES_H_
 
+#include <stdint.h>
 #include <exceptions/exceptions.h>
 
 namespace isc {

+ 1 - 0
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;

+ 22 - 2
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<typename T>
     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<T>(type, buf.begin(), buf.begin() + buf.size()));
+        OptionPtr option(new Option6Int<T>(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<typename T>
+    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<T>::len != 0) {
+            isc_throw(isc::OutOfRange, "option buffer length must be multiple of "
+                      << OptionDataTypes<T>::len << " bytes");
+        }
+        OptionPtr option(new Option6IntArray<T>(type, buf.begin(), buf.end()));
         return (option);
     }
 

+ 82 - 1
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<bool>(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<uint16_t>;
+    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<Option6IntArray<uint16_t> > option_cast_v6 =
+        boost::static_pointer_cast<Option6IntArray<uint16_t> >(option_v6);
+    // Get the values from the initiated options and validate.
+    Option6IntArray<uint16_t>::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<uint32_t>;
+    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<Option6IntArray<uint32_t> > option_cast_v6 =
+        boost::static_pointer_cast<Option6IntArray<uint32_t> >(option_v6);
+    // Get the values from the initiated options and validate.
+    Option6IntArray<uint32_t>::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