Browse Source

[2526] OptionInt can be now used for V4 and V6.

Marcin Siodelski 12 years ago
parent
commit
351dbdab09

+ 28 - 15
src/lib/dhcp/option.cc

@@ -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_);
@@ -139,6 +132,26 @@ void Option::pack6(isc::util::OutputBuffer& buf) {
 }
 
 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_) {
     case V4:

+ 16 - 0
src/lib/dhcp/option.h

@@ -312,6 +312,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

+ 3 - 2
src/lib/dhcp/option_definition.h

@@ -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,10 +352,10 @@ 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 OptionInt<T>(type, begin, end));
+        OptionPtr option(new OptionInt<T>(u, type, begin, end));
         return (option);
     }
 

+ 13 - 8
src/lib/dhcp/option_int.h

@@ -41,13 +41,14 @@ 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.
-    OptionInt(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.
-    OptionInt(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 = (universe_ == 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

+ 209 - 66
src/lib/dhcp/tests/option_int_unittest.cc

@@ -31,6 +31,9 @@ using namespace isc::util;
 
 namespace {
 
+/// Option code being used in many test cases.
+const uint16_t TEST_OPT_CODE = 232;
+
 /// @brief OptionInt test class.
 class OptionIntTest : public ::testing::Test {
 public:
@@ -48,22 +51,24 @@ public:
     /// @note this function does not perform type check. Make
     /// sure that only int8_t or uint8_t type is used.
     ///
+    /// @param u universe (V4 or V6).
     /// @tparam T int8_t or uint8_t.
     template<typename T>
-    void basicTest8() {
+    void basicTest8(const Option::Universe u) {
         // Create option that conveys single 8 bit integer value.
         boost::shared_ptr<OptionInt<T> > opt;
         // Initialize buffer with this value.
         buf_[0] = 0xa1;
         // Constructor may throw in case provided buffer is too short.
         ASSERT_NO_THROW(
-            opt = boost::shared_ptr<OptionInt<T> >(new OptionInt<T>(D6O_PREFERENCE,
-                                                                      buf_.begin(),
-                                                                      buf_.end()))
+            opt = boost::shared_ptr<OptionInt<T> >(new OptionInt<T>(u,
+                                                                    TEST_OPT_CODE,
+                                                                    buf_.begin(),
+                                                                    buf_.begin() + 1))
         );
 
-        EXPECT_EQ(Option::V6, opt->getUniverse());
-        EXPECT_EQ(D6O_PREFERENCE, opt->getType());
+        EXPECT_EQ(u, opt->getUniverse());
+        EXPECT_EQ(TEST_OPT_CODE, opt->getType());
         // Option should return the same value that we initialized the first
         // byte of the buffer with.
         EXPECT_EQ(static_cast<T>(0xa1), opt->getValue());
@@ -73,16 +78,28 @@ public:
 
         // Data length is 1 byte.
         EXPECT_EQ(1, opt->len() - opt->getHeaderLen());
-        EXPECT_EQ(D6O_PREFERENCE, opt->getType());
-        // The total length is 1 byte for data and 4 bytes for header.
-        EXPECT_EQ(5, out_buf_.getLength());
+        EXPECT_EQ(TEST_OPT_CODE, opt->getType());
+        // The total length is 1 byte for data and 2 bytes or 4 bytes
+        // for option code and option length.
+        if (u == Option::V4) {
+            EXPECT_EQ(3, out_buf_.getLength());
+        } else {
+            EXPECT_EQ(5, out_buf_.getLength());
+        }
 
         // Check if pack worked properly:
         InputBuffer out(out_buf_.getData(), out_buf_.getLength());
-        // if option type is correct
-        EXPECT_EQ(D6O_PREFERENCE, out.readUint16());
-        // if option length is correct
-        EXPECT_EQ(1, out.readUint16());
+        if (u == Option::V4) {
+            // if option type is correct
+            EXPECT_EQ(TEST_OPT_CODE, out.readUint8());
+            // if option length is correct
+            EXPECT_EQ(1, out.readUint8());
+        } else {
+            // if option type is correct
+            EXPECT_EQ(TEST_OPT_CODE, out.readUint16());
+            // if option length is correct
+            EXPECT_EQ(1, out.readUint16());
+        }
         // if data is correct
         EXPECT_EQ(0xa1, out.readUint8() );
     }
@@ -92,9 +109,10 @@ public:
     /// @note this function does not perform type check. Make
     /// sure that only int16_t or uint16_t type is used.
     ///
+    /// @param u universe (V4 or V6)
     /// @tparam T int16_t or uint16_t.
     template<typename T>
-    void basicTest16() {
+    void basicTest16(const Option::Universe u) {
         // Create option that conveys single 16-bit integer value.
         boost::shared_ptr<OptionInt<T> > opt;
         // Initialize buffer with uint16_t value.
@@ -102,13 +120,14 @@ public:
         buf_[1] = 0xa2;
         // Constructor may throw in case provided buffer is too short.
         ASSERT_NO_THROW(
-            opt = boost::shared_ptr<OptionInt<T> >(new OptionInt<T>(D6O_ELAPSED_TIME,
-                                                                      buf_.begin(),
-                                                                      buf_.end()))
+            opt = boost::shared_ptr<OptionInt<T> >(new OptionInt<T>(u,
+                                                                    TEST_OPT_CODE,
+                                                                    buf_.begin(),
+                                                                    buf_.begin() + 2))
         );
 
-        EXPECT_EQ(Option::V6, opt->getUniverse());
-        EXPECT_EQ(D6O_ELAPSED_TIME, opt->getType());
+        EXPECT_EQ(u, opt->getUniverse());
+        EXPECT_EQ(TEST_OPT_CODE, opt->getType());
         // Option should return the value equal to the contents of first
         // and second byte of the buffer.
         EXPECT_EQ(static_cast<T>(0xa1a2), opt->getValue());
@@ -118,16 +137,27 @@ public:
 
         // Data length is 2 bytes.
         EXPECT_EQ(2, opt->len() - opt->getHeaderLen());
-        EXPECT_EQ(D6O_ELAPSED_TIME, opt->getType());
-        // The total length is 2 byte for data and 4 bytes for header.
-        EXPECT_EQ(6, out_buf_.getLength());
+        EXPECT_EQ(TEST_OPT_CODE, opt->getType());
+        // The total length is 2 bytes for data and 2 or 4 bytes for aheader.
+        if (u == Option::V4) {
+            EXPECT_EQ(4, out_buf_.getLength());
+        } else {
+            EXPECT_EQ(6, out_buf_.getLength());
+        }
 
         // Check if pack worked properly:
         InputBuffer out(out_buf_.getData(), out_buf_.getLength());
-        // if option type is correct
-        EXPECT_EQ(D6O_ELAPSED_TIME, out.readUint16());
-        // if option length is correct
-        EXPECT_EQ(2, out.readUint16());
+        if (u == Option::V4) {
+            // if option type is correct
+            EXPECT_EQ(TEST_OPT_CODE, out.readUint8());
+            // if option length is correct
+            EXPECT_EQ(2, out.readUint8());
+        } else {
+            // if option type is correct
+            EXPECT_EQ(TEST_OPT_CODE, out.readUint16());
+            // if option length is correct
+            EXPECT_EQ(2, out.readUint16());
+        }
         // if data is correct
         EXPECT_EQ(0xa1a2, out.readUint16() );
     }
@@ -137,9 +167,10 @@ public:
     /// @note this function does not perform type check. Make
     /// sure that only int32_t or uint32_t type is used.
     ///
+    /// @param u universe (V4 or V6).
     /// @tparam T int32_t or uint32_t.
     template<typename T>
-    void basicTest32() {
+    void basicTest32(const Option::Universe u) {
         // Create option that conveys single 32-bit integer value.
         boost::shared_ptr<OptionInt<T> > opt;
         // Initialize buffer with 32-bit integer value.
@@ -149,13 +180,14 @@ public:
         buf_[3] = 0xa4;
         // Constructor may throw in case provided buffer is too short.
         ASSERT_NO_THROW(
-                        opt = boost::shared_ptr<OptionInt<T> >(new OptionInt<T>(D6O_CLT_TIME,
-                                                                                  buf_.begin(),
-                                                                                  buf_.end()))
-                        );
+            opt = boost::shared_ptr<OptionInt<T> >(new OptionInt<T>(u,
+                                                                    TEST_OPT_CODE,
+                                                                    buf_.begin(),
+                                                                    buf_.begin() + 4))
+        );
 
-        EXPECT_EQ(Option::V6, opt->getUniverse());
-        EXPECT_EQ(D6O_CLT_TIME, opt->getType());
+        EXPECT_EQ(u, opt->getUniverse());
+        EXPECT_EQ(TEST_OPT_CODE, opt->getType());
         // Option should return the value equal to the value made of
         // first 4 bytes of the buffer.
         EXPECT_EQ(static_cast<T>(0xa1a2a3a4), opt->getValue());
@@ -165,16 +197,27 @@ public:
 
         // Data length is 4 bytes.
         EXPECT_EQ(4, opt->len() - opt->getHeaderLen());
-        EXPECT_EQ(D6O_CLT_TIME, opt->getType());
-        // The total length is 4 bytes for data and 4 bytes for header.
-        EXPECT_EQ(8, out_buf_.getLength());
+        EXPECT_EQ(TEST_OPT_CODE, opt->getType());
+        // The total length is 4 bytes for data and 2 or 4 bytes for a header.
+        if (u == Option::V4) {
+            EXPECT_EQ(6, out_buf_.getLength());
+        } else {
+            EXPECT_EQ(8, out_buf_.getLength());
+        }
 
         // Check if pack worked properly:
         InputBuffer out(out_buf_.getData(), out_buf_.getLength());
-        // if option type is correct
-        EXPECT_EQ(D6O_CLT_TIME, out.readUint16());
-        // if option length is correct
-        EXPECT_EQ(4, out.readUint16());
+        if (u == Option::V4) {
+            // if option type is correct
+            EXPECT_EQ(TEST_OPT_CODE, out.readUint8());
+            // if option length is correct
+            EXPECT_EQ(4, out.readUint8());
+        } else {
+            // if option type is correct
+            EXPECT_EQ(TEST_OPT_CODE, out.readUint16());
+            // if option length is correct
+            EXPECT_EQ(4, out.readUint16());
+        }
         // if data is correct
         EXPECT_EQ(0xa1a2a3a4, out.readUint32());
     }
@@ -189,43 +232,70 @@ public:
 
 TEST_F(OptionIntTest, useInvalidType) {
     EXPECT_THROW(
-        boost::scoped_ptr<OptionInt<bool> >(new OptionInt<bool>(D6O_ELAPSED_TIME, 10)),
+        boost::scoped_ptr<OptionInt<bool> >(new OptionInt<bool>(Option::V6,
+                                                                D6O_ELAPSED_TIME, 10)),
         InvalidDataType
     );
 
     EXPECT_THROW(
-        boost::scoped_ptr<OptionInt<int64_t> >(new OptionInt<int64_t>(D6O_ELAPSED_TIME, 10)),
+        boost::scoped_ptr<OptionInt<int64_t> >(new OptionInt<int64_t>(Option::V6,
+                                                                      D6O_ELAPSED_TIME, 10)),
         InvalidDataType
     );
 
 }
 
-TEST_F(OptionIntTest, basicUint8) {
-    basicTest8<uint8_t>();
+TEST_F(OptionIntTest, basicUint8V4) {
+    basicTest8<uint8_t>(Option::V4);
+}
+
+TEST_F(OptionIntTest, basicUint8V6) {
+    basicTest8<uint8_t>(Option::V6);
+}
+
+TEST_F(OptionIntTest, basicUint16V4) {
+    basicTest16<uint16_t>(Option::V4);
+}
+
+TEST_F(OptionIntTest, basicUint16V6) {
+    basicTest16<uint16_t>(Option::V6);
 }
 
-TEST_F(OptionIntTest, basicUint16) {
-    basicTest16<uint16_t>();
+TEST_F(OptionIntTest, basicUint32V4) {
+    basicTest32<uint32_t>(Option::V4);
 }
 
-TEST_F(OptionIntTest, basicUint32) {
-    basicTest32<uint32_t>();
+TEST_F(OptionIntTest, basicUint32V6) {
+    basicTest32<uint32_t>(Option::V6);
 }
 
-TEST_F(OptionIntTest, basicInt8) {
-    basicTest8<int8_t>();
+TEST_F(OptionIntTest, basicInt8V4) {
+    basicTest8<int8_t>(Option::V4);
 }
 
-TEST_F(OptionIntTest, basicInt16) {
-    basicTest16<int16_t>();
+TEST_F(OptionIntTest, basicInt8V6) {
+    basicTest8<int8_t>(Option::V6);
 }
 
-TEST_F(OptionIntTest, basicInt32) {
-    basicTest32<int32_t>();
+TEST_F(OptionIntTest, basicInt16V4) {
+    basicTest16<int16_t>(Option::V4);
+}
+
+TEST_F(OptionIntTest, basicInt16V6) {
+    basicTest16<int16_t>(Option::V6);
+}
+
+TEST_F(OptionIntTest, basicInt32V4) {
+    basicTest32<int32_t>(Option::V4);
+}
+
+TEST_F(OptionIntTest, basicInt32V6) {
+    basicTest32<int32_t>(Option::V6);
 }
 
 TEST_F(OptionIntTest, setValueUint8) {
-    boost::shared_ptr<OptionInt<uint8_t> > opt(new OptionInt<uint8_t>(D6O_PREFERENCE, 123));
+    boost::shared_ptr<OptionInt<uint8_t> > opt(new OptionInt<uint8_t>(Option::V6,
+                                                                      D6O_PREFERENCE, 123));
     // Check if constructor intitialized the option value correctly.
     EXPECT_EQ(123, opt->getValue());
     // Override the value.
@@ -238,7 +308,8 @@ TEST_F(OptionIntTest, setValueUint8) {
 }
 
 TEST_F(OptionIntTest, setValueInt8) {
-    boost::shared_ptr<OptionInt<int8_t> > opt(new OptionInt<int8_t>(D6O_PREFERENCE, -123));
+    boost::shared_ptr<OptionInt<int8_t> > opt(new OptionInt<int8_t>(Option::V6,
+                                                                    D6O_PREFERENCE, -123));
     // Check if constructor intitialized the option value correctly.
     EXPECT_EQ(-123, opt->getValue());
     // Override the value.
@@ -252,7 +323,8 @@ TEST_F(OptionIntTest, setValueInt8) {
 
 
 TEST_F(OptionIntTest, setValueUint16) {
-    boost::shared_ptr<OptionInt<uint16_t> > opt(new OptionInt<uint16_t>(D6O_ELAPSED_TIME, 123));
+    boost::shared_ptr<OptionInt<uint16_t> > opt(new OptionInt<uint16_t>(Option::V6,
+                                                                        D6O_ELAPSED_TIME, 123));
     // Check if constructor intitialized the option value correctly.
     EXPECT_EQ(123, opt->getValue());
     // Override the value.
@@ -265,7 +337,8 @@ TEST_F(OptionIntTest, setValueUint16) {
 }
 
 TEST_F(OptionIntTest, setValueInt16) {
-    boost::shared_ptr<OptionInt<int16_t> > opt(new OptionInt<int16_t>(D6O_ELAPSED_TIME, -16500));
+    boost::shared_ptr<OptionInt<int16_t> > opt(new OptionInt<int16_t>(Option::V6,
+                                                                      D6O_ELAPSED_TIME, -16500));
     // Check if constructor intitialized the option value correctly.
     EXPECT_EQ(-16500, opt->getValue());
     // Override the value.
@@ -278,7 +351,8 @@ TEST_F(OptionIntTest, setValueInt16) {
 }
 
 TEST_F(OptionIntTest, setValueUint32) {
-    boost::shared_ptr<OptionInt<uint32_t> > opt(new OptionInt<uint32_t>(D6O_CLT_TIME, 123));
+    boost::shared_ptr<OptionInt<uint32_t> > opt(new OptionInt<uint32_t>(Option::V6,
+                                                                        D6O_CLT_TIME, 123));
     // Check if constructor intitialized the option value correctly.
     EXPECT_EQ(123, opt->getValue());
     // Override the value.
@@ -290,8 +364,9 @@ TEST_F(OptionIntTest, setValueUint32) {
     EXPECT_EQ(0x01020304, opt->getValue());
 }
 
-TEST_F(OptionIntTest, setValueint32) {
-    boost::shared_ptr<OptionInt<int32_t> > opt(new OptionInt<int32_t>(D6O_CLT_TIME, -120100));
+TEST_F(OptionIntTest, setValueInt32) {
+    boost::shared_ptr<OptionInt<int32_t> > opt(new OptionInt<int32_t>(Option::V6,
+                                                                      D6O_CLT_TIME, -120100));
     // Check if constructor intitialized the option value correctly.
     EXPECT_EQ(-120100, opt->getValue());
     // Override the value.
@@ -303,12 +378,41 @@ TEST_F(OptionIntTest, setValueint32) {
     EXPECT_EQ(-125000, opt->getValue());
 }
 
-TEST_F(OptionIntTest, packSuboptions) {
+TEST_F(OptionIntTest, packSuboptions4) {
+    boost::shared_ptr<OptionInt<uint16_t> > opt(new OptionInt<uint16_t>(Option::V4,
+                                                                        TEST_OPT_CODE,
+                                                                        0x0102));
+    // Add sub option with some 4 bytes of data (each byte set to 1)
+    OptionPtr sub1(new Option(Option::V4, TEST_OPT_CODE + 1, OptionBuffer(4, 1)));
+    // Add sub option with some 5 bytes of data (each byte set to 2)
+    OptionPtr sub2(new Option(Option::V4, TEST_OPT_CODE + 2, OptionBuffer(5, 2)));
+
+    // Add suboptions.
+    opt->addOption(sub1);
+    opt->addOption(sub2);
+
+    // Prepare reference data: option + suoptions in wire format.
+    uint8_t expected[] = {
+        TEST_OPT_CODE, 15, // option header
+        0x01, 0x02,        // data, uint16_t value = 0x0102
+        TEST_OPT_CODE + 1, 0x04, 0x01, 0x01, 0x01, 0x01, // sub1
+        TEST_OPT_CODE + 2, 0x05, 0x02, 0x02, 0x02, 0x02, 0x02 // sub2
+    };
+
+    // Create on-wire format of option and suboptions.
+    opt->pack(out_buf_);
+    // Compare the on-wire data with the reference buffer.
+    ASSERT_EQ(sizeof(expected), out_buf_.getLength());
+    EXPECT_EQ(0, memcmp(out_buf_.getData(), expected, sizeof(expected)));
+}
+
+TEST_F(OptionIntTest, packSuboptions6) {
     // option code is really uint16_t, but using uint8_t
     // for easier conversion to uint8_t array.
     uint8_t opt_code = 80;
 
-    boost::shared_ptr<OptionInt<uint32_t> > opt(new OptionInt<uint32_t>(opt_code, 0x01020304));
+    boost::shared_ptr<OptionInt<uint32_t> > opt(new OptionInt<uint32_t>(Option::V6,
+                                                                        opt_code, 0x01020304));
     OptionPtr sub1(new Option(Option::V6, 0xcafe));
 
     boost::shared_ptr<Option6IAAddr> addr1(
@@ -346,8 +450,46 @@ TEST_F(OptionIntTest, packSuboptions) {
     EXPECT_EQ(0, memcmp(out_buf_.getData(), expected, 40));
 }
 
+TEST_F(OptionIntTest, unpackSuboptions4) {
+    // Prepare reference data.
+    const uint8_t expected[] = {
+        TEST_OPT_CODE, 0x0A, // option code and length
+        0x01, 0x02, 0x03, 0x04, // data, uint32_t value = 0x01020304
+        TEST_OPT_CODE + 1, 0x4, 0x01, 0x01, 0x01, 0x01 // suboption
+    };
+    // Copy the data to a vector so as we can pas it to the
+    // OptionInt's constructor.
+    memcpy(&buf_[0], expected, sizeof(expected));
+
+    // Create an option.
+    boost::shared_ptr<OptionInt<uint32_t> > opt;
+    EXPECT_NO_THROW(
+        opt = boost::shared_ptr<
+            OptionInt<uint32_t> >(new OptionInt<uint32_t>(Option::V4, TEST_OPT_CODE,
+                                                          buf_.begin() + 2,
+                                                          buf_.begin() + sizeof(expected)));
+    );
+    ASSERT_TRUE(opt);
+
+    // Verify that it has expected type and data.
+    EXPECT_EQ(TEST_OPT_CODE, opt->getType());
+    EXPECT_EQ(0x01020304, opt->getValue());
+
+    // Expect that there is the sub option with the particular
+    // option code added.
+    OptionPtr subopt = opt->getOption(TEST_OPT_CODE + 1);
+    ASSERT_TRUE(subopt);
+    // Check that this option has correct universe and code.
+    EXPECT_EQ(Option::V4, subopt->getUniverse());
+    EXPECT_EQ(TEST_OPT_CODE + 1, subopt->getType());
+    // Check the sub option's data.
+    OptionBuffer subopt_buf = subopt->getData();
+    ASSERT_EQ(4, subopt_buf.size());
+    // The data in the input buffer starts at offset 8.
+    EXPECT_TRUE(std::equal(subopt_buf.begin(), subopt_buf.end(), buf_.begin() + 8));
+}
 
-TEST_F(OptionIntTest, unpackSuboptions) {
+TEST_F(OptionIntTest, unpackSuboptions6) {
     // option code is really uint16_t, but using uint8_t
     // for easier conversion to uint8_t array.
     const uint8_t opt_code = 80;
@@ -376,8 +518,9 @@ TEST_F(OptionIntTest, unpackSuboptions) {
     boost::shared_ptr<OptionInt<uint16_t> > opt;
     EXPECT_NO_THROW(
         opt = boost::shared_ptr<
-            OptionInt<uint16_t> >(new OptionInt<uint16_t>(opt_code, buf_.begin() + 4,
-                                                            buf_.begin() + sizeof(expected)));
+            OptionInt<uint16_t> >(new OptionInt<uint16_t>(Option::V6, opt_code,
+                                                          buf_.begin() + 4,
+                                                          buf_.begin() + sizeof(expected)));
     );
     ASSERT_TRUE(opt);