Browse Source

[master] Merge branch 'trac3145' (IA_PD and IAPREFIX DHCPv6 options)

Conflicts:
	ChangeLog
Tomek Mrugalski 11 years ago
parent
commit
bf6f8ee24c

+ 7 - 2
ChangeLog

@@ -1,7 +1,12 @@
+673.	[func]		tomek
+	libdhcp: Added support for IA_PD and IAPREFIX options. New class
+	for IAPREFIX (Option6_IAPrefix) has been added.
+	(Trac #3145, git 3a844e85ecc3067ccd1c01841f4a61366cb278f4)
+
 672.	[func]	tmark
 	Added b10-dhcp-ddnsupdate transaction base class, NameChangeTransaction.
-	This class provides the common structure and methods to implement the state 
-	models described in the DHCP_DDNS design, plus integration with DNSClient 
+	This class provides the common structure and methods to implement the state
+	models described in the DHCP_DDNS design, plus integration with DNSClient
 	and its callback mechanism for asynchronous IO with the DNS servers.
 	(Trac #3086, git 079b862c9eb21056fdf957e560b8fe7b218441b6)
 

+ 1 - 0
src/lib/dhcp/Makefile.am

@@ -26,6 +26,7 @@ libb10_dhcp___la_SOURCES += option4_addrlst.cc option4_addrlst.h
 libb10_dhcp___la_SOURCES += option4_client_fqdn.cc option4_client_fqdn.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_iaprefix.cc option6_iaprefix.h
 libb10_dhcp___la_SOURCES += option6_addrlst.cc option6_addrlst.h
 libb10_dhcp___la_SOURCES += option6_client_fqdn.cc option6_client_fqdn.h
 libb10_dhcp___la_SOURCES += option_int.h

+ 15 - 1
src/lib/dhcp/option6_ia.cc

@@ -30,10 +30,24 @@ namespace dhcp {
 
 Option6IA::Option6IA(uint16_t type, uint32_t iaid)
     :Option(Option::V6, type), iaid_(iaid), t1_(0), t2_(0) {
+
+    // IA_TA has different layout than IA_NA and IA_PD. We can't sue this class
+    if (type == D6O_IA_TA) {
+        isc_throw(BadValue, "Can't use Option6IA for IA_TA as it has "
+                  "a different layout");
+    }
 }
 
-Option6IA::Option6IA(uint16_t type, OptionBufferConstIter begin, OptionBufferConstIter end)
+Option6IA::Option6IA(uint16_t type, OptionBufferConstIter begin,
+                     OptionBufferConstIter end)
     :Option(Option::V6, type) {
+
+    // IA_TA has different layout than IA_NA and IA_PD. We can't use this class
+    if (type == D6O_IA_TA) {
+        isc_throw(BadValue, "Can't use Option6IA for IA_TA as it has "
+                  "a different layout");
+    }
+
     unpack(begin, end);
 }
 

+ 3 - 0
src/lib/dhcp/option6_iaaddr.cc

@@ -35,6 +35,9 @@ Option6IAAddr::Option6IAAddr(uint16_t type, const isc::asiolink::IOAddress& addr
                              uint32_t pref, uint32_t valid)
     :Option(V6, type), addr_(addr), preferred_(pref),
      valid_(valid) {
+    if (!addr.isV6()) {
+        isc_throw(isc::BadValue, addr_.toText() << " is not an IPv6 address");
+    }
 }
 
 Option6IAAddr::Option6IAAddr(uint32_t type, OptionBuffer::const_iterator begin,

+ 6 - 2
src/lib/dhcp/option6_iaaddr.h

@@ -33,7 +33,9 @@ public:
     /// length of the fixed part of the IAADDR option
     static const size_t OPTION6_IAADDR_LEN = 24;
 
-    /// @brief Ctor, used for options constructed (during transmission).
+    /// @brief Constructor, used for options constructed (during transmission).
+    ///
+    /// @throw BadValue if specified addr is not IPv6
     ///
     /// @param type option type
     /// @param addr reference to an address
@@ -42,7 +44,9 @@ public:
     Option6IAAddr(uint16_t type, const isc::asiolink::IOAddress& addr,
                   uint32_t preferred, uint32_t valid);
 
-    /// @brief ctor, used for received options.
+    /// @brief Constructor, used for received options.
+    ///
+    /// @throw OutOfRange if specified option is truncated
     ///
     /// @param type option type
     /// @param begin iterator to first byte of option data

+ 124 - 0
src/lib/dhcp/option6_iaprefix.cc

@@ -0,0 +1,124 @@
+// Copyright (C) 2013 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 <asiolink/io_address.h>
+#include <dhcp/dhcp6.h>
+#include <dhcp/libdhcp++.h>
+#include <dhcp/option6_iaprefix.h>
+#include <exceptions/exceptions.h>
+#include <util/io_utilities.h>
+
+#include <sstream>
+
+#include <stdint.h>
+#include <arpa/inet.h>
+
+using namespace std;
+using namespace isc::asiolink;
+using namespace isc::util;
+
+namespace isc {
+namespace dhcp {
+
+Option6IAPrefix::Option6IAPrefix(uint16_t type, const isc::asiolink::IOAddress& prefix,
+                                 uint8_t prefix_len, uint32_t pref, uint32_t valid)
+    :Option6IAAddr(type, prefix, pref, valid), prefix_len_(prefix_len) {
+    // Option6IAAddr will check if prefix is IPv6 and will throw if it is not
+    if (prefix_len > 128) {
+        isc_throw(BadValue, prefix_len << " is not a valid prefix length. "
+                  << "Allowed range is 0..128");
+    }
+}
+
+Option6IAPrefix::Option6IAPrefix(uint32_t type, OptionBuffer::const_iterator begin,
+                             OptionBuffer::const_iterator end)
+    :Option6IAAddr(type, begin, end) {
+    unpack(begin, end);
+}
+
+void Option6IAPrefix::pack(isc::util::OutputBuffer& buf) {
+    if (!addr_.isV6()) {
+        isc_throw(isc::BadValue, addr_.toText() << " is not an IPv6 address");
+    }
+
+    buf.writeUint16(type_);
+
+    // len() returns complete option length. len field contains
+    // length without 4-byte option header
+    buf.writeUint16(len() - getHeaderLen());
+
+    buf.writeUint32(preferred_);
+    buf.writeUint32(valid_);
+    buf.writeUint8(prefix_len_);
+
+    buf.writeData(&addr_.toBytes()[0], isc::asiolink::V6ADDRESS_LEN);
+
+    // store encapsulated options (the only defined so far is PD_EXCLUDE)
+    packOptions(buf);
+}
+
+void Option6IAPrefix::unpack(OptionBuffer::const_iterator begin,
+                      OptionBuffer::const_iterator end) {
+    if ( distance(begin, end) < OPTION6_IAPREFIX_LEN) {
+        isc_throw(OutOfRange, "Option " << type_ << " truncated");
+    }
+
+    preferred_ = readUint32( &(*begin) );
+    begin += sizeof(uint32_t);
+
+    valid_ = readUint32( &(*begin) );
+    begin += sizeof(uint32_t);
+
+    prefix_len_ = *begin;
+    begin += sizeof(uint8_t);
+
+    // 16 bytes: IPv6 address
+    addr_ = IOAddress::fromBytes(AF_INET6, &(*begin));
+    begin += V6ADDRESS_LEN;
+
+    // unpack encapsulated options (the only defined so far is PD_EXCLUDE)
+    unpackOptions(OptionBuffer(begin, end));
+}
+
+std::string Option6IAPrefix::toText(int indent /* =0 */) {
+    stringstream tmp;
+    for (int i=0; i<indent; i++)
+        tmp << " ";
+
+    tmp << "type=" << type_ << "(IAPREFIX) prefix=" << addr_.toText() << "/"
+        << prefix_len_ << ", preferred-lft=" << preferred_ << ", valid-lft="
+        << valid_ << endl;
+
+    for (OptionCollection::const_iterator opt=options_.begin();
+         opt!=options_.end();
+         ++opt) {
+        tmp << (*opt).second->toText(indent + 2);
+    }
+    return tmp.str();
+}
+
+uint16_t Option6IAPrefix::len() {
+
+    uint16_t length = OPTION6_HDR_LEN + OPTION6_IAPREFIX_LEN;
+
+    // length of all suboptions
+    for (Option::OptionCollection::const_iterator it = options_.begin();
+         it != options_.end(); ++it) {
+        length += (*it).second->len();
+    }
+    return (length);
+}
+
+} // end of namespace isc::dhcp
+} // end of namespace isc

+ 109 - 0
src/lib/dhcp/option6_iaprefix.h

@@ -0,0 +1,109 @@
+// Copyright (C) 2013 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_IAPREFIX_H
+#define OPTION6_IAPREFIX_H
+
+#include <asiolink/io_address.h>
+#include <dhcp/option6_iaaddr.h>
+#include <dhcp/option.h>
+
+namespace isc {
+namespace dhcp {
+
+
+/// @brief Class that represents IAPREFIX option in DHCPv6
+///
+/// It is based on a similar class that handles addresses. There are major
+/// differences, though. The fields are in different order. There is also
+/// additional prefix length field.
+///
+/// It should be noted that to get a full prefix (2 values: base prefix, and
+/// a prefix length) 2 methods are used: getAddress() and getLength(). Although
+/// using getAddress() to obtain base prefix is somewhat counter-intuitive at
+/// first, it becomes obvious when one realizes that an address is a special
+/// case of a prefix with /128. It makes everyone's like much easier, because
+/// the base prefix doubles as a regular address in many cases, e.g. when
+/// searching for a lease.
+class Option6IAPrefix : public Option6IAAddr {
+
+public:
+    /// length of the fixed part of the IAPREFIX option
+    static const size_t OPTION6_IAPREFIX_LEN = 25;
+
+    /// @brief Constructor, used for options constructed (during transmission).
+    ///
+    /// @param type option type
+    /// @param addr reference to an address
+    /// @param prefix_length length (1-128)
+    /// @param preferred address preferred lifetime (in seconds)
+    /// @param valid address valid lifetime (in seconds)
+    Option6IAPrefix(uint16_t type, const isc::asiolink::IOAddress& addr,
+                    uint8_t prefix_length, uint32_t preferred, uint32_t valid);
+
+    /// @brief Constructor, used for received options.
+    ///
+    /// @throw OutOfRange if buffer is too short
+    ///
+    /// @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)
+    Option6IAPrefix(uint32_t type, OptionBuffer::const_iterator begin,
+                    OptionBuffer::const_iterator end);
+
+    /// @brief Writes option in wire-format.
+    ///
+    /// Writes option in wire-format to buf, returns pointer to first unused
+    /// byte after stored option.
+    ///
+    /// @throw BadValue if the address is not IPv6
+    ///
+    /// @param buf pointer to a buffer
+    void pack(isc::util::OutputBuffer& buf);
+
+    /// @brief Parses received buffer.
+    ///
+    /// @throw OutOfRange when buffer is shorter than 25 bytes
+    ///
+    /// @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 printing text
+    ///
+    /// @return string with text representation.
+    virtual std::string toText(int indent = 0);
+
+    /// sets address in this option.
+    ///
+    /// @param addr address to be sent in this option
+    void setPrefix(const isc::asiolink::IOAddress& prefix,
+                   uint8_t length) { addr_ = prefix; prefix_len_ = length; }
+
+    uint8_t getLength() const { return prefix_len_; }
+
+    /// returns data length (data length + DHCPv4/DHCPv6 option header)
+    virtual uint16_t len();
+
+protected:
+    uint8_t prefix_len_;
+};
+
+} // isc::dhcp namespace
+} // isc namespace
+
+#endif // OPTION_IA_H

+ 1 - 1
src/lib/dhcp/std_option_defs.h

@@ -214,7 +214,7 @@ RECORD_DECL(IA_NA_RECORDS, OPT_UINT32_TYPE, OPT_UINT32_TYPE, OPT_UINT32_TYPE);
 RECORD_DECL(IA_PD_RECORDS, OPT_UINT32_TYPE, OPT_UINT32_TYPE, OPT_UINT32_TYPE);
 // ia-prefix
 RECORD_DECL(IA_PREFIX_RECORDS, OPT_UINT32_TYPE, OPT_UINT32_TYPE,
-            OPT_UINT8_TYPE, OPT_BINARY_TYPE);
+            OPT_UINT8_TYPE, OPT_IPV6_ADDRESS_TYPE, OPT_BINARY_TYPE);
 // lq-query
 RECORD_DECL(LQ_QUERY_RECORDS, OPT_UINT8_TYPE, OPT_IPV6_ADDRESS_TYPE);
 // lq-relay-data

+ 1 - 0
src/lib/dhcp/tests/Makefile.am

@@ -36,6 +36,7 @@ libdhcp___unittests_SOURCES += option6_addrlst_unittest.cc
 libdhcp___unittests_SOURCES += option6_client_fqdn_unittest.cc
 libdhcp___unittests_SOURCES += option6_ia_unittest.cc
 libdhcp___unittests_SOURCES += option6_iaaddr_unittest.cc
+libdhcp___unittests_SOURCES += option6_iaprefix_unittest.cc
 libdhcp___unittests_SOURCES += option_int_unittest.cc
 libdhcp___unittests_SOURCES += option_int_array_unittest.cc
 libdhcp___unittests_SOURCES += option_data_types_unittest.cc

+ 130 - 56
src/lib/dhcp/tests/option6_ia_unittest.cc

@@ -18,6 +18,7 @@
 #include <dhcp/option.h>
 #include <dhcp/option6_ia.h>
 #include <dhcp/option6_iaaddr.h>
+#include <dhcp/option6_iaprefix.h>
 #include <util/buffer.h>
 
 #include <boost/scoped_ptr.hpp>
@@ -43,71 +44,96 @@ public:
             buf_[i] = 255 - i;
         }
     }
-    OptionBuffer buf_;
-    OutputBuffer outBuf_;
-};
 
-TEST_F(Option6IATest, basic) {
-    buf_[0] = 0xa1; // iaid
-    buf_[1] = 0xa2;
-    buf_[2] = 0xa3;
-    buf_[3] = 0xa4;
+    /// @brief performs basic checks on IA option
+    ///
+    /// Check that an option can be built based on incoming buffer and that
+    /// the option contains expected values.
+    /// @param type specifies option type (IA_NA or IA_PD)
+    void checkIA(uint16_t type) {
+        buf_[0] = 0xa1; // iaid
+        buf_[1] = 0xa2;
+        buf_[2] = 0xa3;
+        buf_[3] = 0xa4;
+
+        buf_[4] = 0x81; // T1
+        buf_[5] = 0x02;
+        buf_[6] = 0x03;
+        buf_[7] = 0x04;
+
+        buf_[8] = 0x84; // T2
+        buf_[9] = 0x03;
+        buf_[10] = 0x02;
+        buf_[11] = 0x01;
+
+        // Create an option
+        // unpack() is called from constructor
+        scoped_ptr<Option6IA> opt;
+        ASSERT_NO_THROW(opt.reset(new Option6IA(type, buf_.begin(),
+                                                buf_.begin() + 12)));
 
-    buf_[4] = 0x81; // T1
-    buf_[5] = 0x02;
-    buf_[6] = 0x03;
-    buf_[7] = 0x04;
+        EXPECT_EQ(Option::V6, opt->getUniverse());
+        EXPECT_EQ(type, opt->getType());
+        EXPECT_EQ(0xa1a2a3a4, opt->getIAID());
+        EXPECT_EQ(0x81020304, opt->getT1());
+        EXPECT_EQ(0x84030201, opt->getT2());
 
-    buf_[8] = 0x84; // T2
-    buf_[9] = 0x03;
-    buf_[10] = 0x02;
-    buf_[11] = 0x01;
+        // Pack this option again in the same buffer, but in
+        // different place
 
-    // Create an option
-    // unpack() is called from constructor
-    scoped_ptr<Option6IA> opt(new Option6IA(D6O_IA_NA,
-                                            buf_.begin(),
-                                            buf_.begin() + 12));
+        // Test for pack()
+        ASSERT_NO_THROW(opt->pack(outBuf_));
 
-    EXPECT_EQ(Option::V6, opt->getUniverse());
-    EXPECT_EQ(D6O_IA_NA, opt->getType());
-    EXPECT_EQ(0xa1a2a3a4, opt->getIAID());
-    EXPECT_EQ(0x81020304, opt->getT1());
-    EXPECT_EQ(0x84030201, opt->getT2());
+        // 12 bytes header + 4 bytes content
+        EXPECT_EQ(12, opt->len() - opt->getHeaderLen());
+        EXPECT_EQ(type, opt->getType());
 
-    // Pack this option again in the same buffer, but in
-    // different place
+        EXPECT_EQ(16, outBuf_.getLength()); // lenght(IA_NA) = 16
 
-    // Test for pack()
-    opt->pack(outBuf_);
+        // Check if pack worked properly:
+        InputBuffer out(outBuf_.getData(), outBuf_.getLength());
 
-    // 12 bytes header + 4 bytes content
-    EXPECT_EQ(12, opt->len() - opt->getHeaderLen());
-    EXPECT_EQ(D6O_IA_NA, opt->getType());
+        // - if option type is correct
+        EXPECT_EQ(type, out.readUint16());
 
-    EXPECT_EQ(16, outBuf_.getLength()); // lenght(IA_NA) = 16
+        // - if option length is correct
+        EXPECT_EQ(12, out.readUint16());
 
-    // Check if pack worked properly:
-    InputBuffer out(outBuf_.getData(), outBuf_.getLength());
+        // - if iaid is correct
+        EXPECT_EQ(0xa1a2a3a4, out.readUint32() );
 
-    // - if option type is correct
-    EXPECT_EQ(D6O_IA_NA, out.readUint16());
+        // - if T1 is correct
+        EXPECT_EQ(0x81020304, out.readUint32() );
 
-    // - if option length is correct
-    EXPECT_EQ(12, out.readUint16());
+        // - if T1 is correct
+        EXPECT_EQ(0x84030201, out.readUint32() );
 
-    // - if iaid is correct
-    EXPECT_EQ(0xa1a2a3a4, out.readUint32() );
+        EXPECT_NO_THROW(opt.reset());
+    }
 
-   // - if T1 is correct
-    EXPECT_EQ(0x81020304, out.readUint32() );
+    OptionBuffer buf_;
+    OutputBuffer outBuf_;
+};
 
-    // - if T1 is correct
-    EXPECT_EQ(0x84030201, out.readUint32() );
+TEST_F(Option6IATest, basic) {
+    checkIA(D6O_IA_NA);
+}
 
-    EXPECT_NO_THROW(opt.reset());
+TEST_F(Option6IATest, pdBasic) {
+    checkIA(D6O_IA_PD);
 }
 
+// Check that this class cannot be used for IA_TA (IA_TA has no T1, T2 fields
+// and people tend to think that if it's good for IA_NA and IA_PD, it can
+// be used for IA_TA as well and that is not true)
+TEST_F(Option6IATest, taForbidden) {
+    EXPECT_THROW(Option6IA(D6O_IA_TA, buf_.begin(), buf_.begin() + 50),
+                 BadValue);
+
+    EXPECT_THROW(Option6IA(D6O_IA_TA, 123), BadValue);
+}
+
+// Check that getters/setters are working as expected.
 TEST_F(Option6IATest, simple) {
     scoped_ptr<Option6IA> ia(new Option6IA(D6O_IA_NA, 1234));
 
@@ -131,12 +157,8 @@ TEST_F(Option6IATest, simple) {
     EXPECT_NO_THROW(ia.reset());
 }
 
-
-// test if option can build suboptions
-TEST_F(Option6IATest, suboptions_pack) {
-    buf_[0] = 0xff;
-    buf_[1] = 0xfe;
-    buf_[2] = 0xfc;
+// test if the option can build suboptions
+TEST_F(Option6IATest, suboptionsPack) {
 
     scoped_ptr<Option6IA> ia(new Option6IA(D6O_IA_NA, 0x13579ace));
     ia->setT1(0x2345);
@@ -154,6 +176,7 @@ TEST_F(Option6IATest, suboptions_pack) {
     ASSERT_EQ(4, sub1->len());
     ASSERT_EQ(48, ia->len());
 
+    // This contains expected on-wire format
     uint8_t expected[] = {
         D6O_IA_NA/256, D6O_IA_NA%256, // type
         0, 44, // length
@@ -175,18 +198,69 @@ TEST_F(Option6IATest, suboptions_pack) {
     };
 
     ia->pack(outBuf_);
-    ASSERT_EQ(48, outBuf_.getLength());
 
+    ASSERT_EQ(48, outBuf_.getLength());
     EXPECT_EQ(0, memcmp(outBuf_.getData(), expected, 48));
-
     EXPECT_NO_THROW(ia.reset());
 }
 
+// test if IA_PD option can build IAPREFIX suboptions
+TEST_F(Option6IATest, pdSuboptionsPack) {
+
+    // Let's build IA_PD
+    scoped_ptr<Option6IA> ia;
+    ASSERT_NO_THROW(ia.reset(new Option6IA(D6O_IA_PD, 0x13579ace)));
+    ia->setT1(0x2345);
+    ia->setT2(0x3456);
+
+    // Put some dummy option in it
+    OptionPtr sub1(new Option(Option::V6, 0xcafe));
+
+    // Put a valid IAPREFIX option in it
+    boost::shared_ptr<Option6IAPrefix> addr1(
+        new Option6IAPrefix(D6O_IAPREFIX, IOAddress("2001:db8:1234:5678::abcd"),
+                            91, 0x5000, 0x7000));
+
+    ia->addOption(sub1);
+    ia->addOption(addr1);
+
+    ASSERT_EQ(29, addr1->len());
+    ASSERT_EQ(4, sub1->len());
+    ASSERT_EQ(49, ia->len());
+
+    uint8_t expected[] = {
+        D6O_IA_PD/256, D6O_IA_PD%256, // type
+        0, 45, // length
+        0x13, 0x57, 0x9a, 0xce, // iaid
+        0, 0, 0x23, 0x45,  // T1
+        0, 0, 0x34, 0x56,  // T2
+
+        // iaprefix suboption
+        D6O_IAPREFIX/256, D6O_IAPREFIX%256, // type
+        0, 25, // len
+        0, 0, 0x50, 0, // preferred-lifetime
+        0, 0, 0x70, 0, // valid-lifetime
+        91, // prefix length
+        0x20, 0x01, 0xd, 0xb8, 0x12,0x34, 0x56, 0x78,
+        0, 0, 0, 0, 0, 0, 0xab, 0xcd, // IP address
+
+        // suboption
+        0xca, 0xfe, // type
+        0, 0 // len
+    };
+
+    ia->pack(outBuf_);
+    ASSERT_EQ(49, outBuf_.getLength());
+
+    EXPECT_EQ(0, memcmp(outBuf_.getData(), expected, 49));
+
+    EXPECT_NO_THROW(ia.reset());
+}
 
 // test if option can parse suboptions
 TEST_F(Option6IATest, suboptions_unpack) {
     // sizeof (expected) = 48 bytes
-    uint8_t expected[] = {
+    const uint8_t expected[] = {
         D6O_IA_NA / 256, D6O_IA_NA % 256, // type
         0, 28, // length
         0x13, 0x57, 0x9a, 0xce, // iaid

+ 15 - 0
src/lib/dhcp/tests/option6_iaaddr_unittest.cc

@@ -106,4 +106,19 @@ TEST_F(Option6IAAddrTest, basic) {
     EXPECT_NO_THROW(opt.reset());
 }
 
+/// @todo: Write test for (type, addr, pref, valid) constructor
+/// See option6_iaprefix_unittest.cc for similar test
+
+// Tests if broken usage causes exception to be thrown
+TEST_F(Option6IAAddrTest, negative) {
+
+    // Too short. Minimum length is 24
+    EXPECT_THROW(Option6IAAddr(D6O_IAADDR, buf_.begin(), buf_.begin() + 23),
+                 OutOfRange);
+
+    // This option is for IPv6 addresses only
+    EXPECT_THROW(Option6IAAddr(D6O_IAADDR, isc::asiolink::IOAddress("192.0.2.1"),
+                               1000, 2000), BadValue);
+}
+
 }

+ 188 - 0
src/lib/dhcp/tests/option6_iaprefix_unittest.cc

@@ -0,0 +1,188 @@
+// Copyright (C) 2011-2013 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 <dhcp/dhcp6.h>
+#include <dhcp/option.h>
+#include <dhcp/option6_iaprefix.h>
+#include <util/buffer.h>
+
+#include <boost/scoped_ptr.hpp>
+#include <gtest/gtest.h>
+
+#include <iostream>
+#include <sstream>
+
+#include <arpa/inet.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::util;
+using namespace isc::asiolink;
+
+namespace {
+class Option6IAPrefixTest : public ::testing::Test {
+public:
+    Option6IAPrefixTest() : buf_(255), outBuf_(255) {
+        for (int i = 0; i < 255; i++) {
+            buf_[i] = 255 - i;
+        }
+    }
+
+    /// @brief creates on-wire representation of IAPREFIX option
+    ///
+    /// buf_ field is set up to have IAPREFIX with preferred=1000,
+    /// valid=3000000000 and prefix beign 2001:db8:1::dead:beef/77
+    void setExampleBuffer() {
+        for (int i = 0; i < 255; i++) {
+            buf_[i] = 0;
+        }
+
+        buf_[ 0] = 0x00;
+        buf_[ 1] = 0x00;
+        buf_[ 2] = 0x03;
+        buf_[ 3] = 0xe8; // preferred lifetime = 1000
+
+        buf_[ 4]  = 0xb2;
+        buf_[ 5] = 0xd0;
+        buf_[ 6] = 0x5e;
+        buf_[ 7] = 0x00; // valid lifetime = 3,000,000,000
+
+        buf_[ 8] = 77; // Prefix length = 77
+
+        buf_[ 9] = 0x20;
+        buf_[10] = 0x01;
+        buf_[11] = 0x0d;
+        buf_[12] = 0xb8;
+        buf_[13] = 0x00;
+        buf_[14] = 0x01;
+        buf_[21] = 0xde;
+        buf_[22] = 0xad;
+        buf_[23] = 0xbe;
+        buf_[24] = 0xef; // 2001:db8:1::dead:beef
+    }
+
+
+    /// @brief Checks whether specified IAPREFIX option meets expected values
+    ///
+    /// To be used with option generated by setExampleBuffer
+    ///
+    /// @param opt IAPREFIX option being tested
+    /// @param expected_type expected option type
+    void checkOption(Option6IAPrefix& opt, uint16_t expected_type) {
+
+        // Check if all fields have expected values
+        EXPECT_EQ(Option::V6, opt.getUniverse());
+        EXPECT_EQ(expected_type, opt.getType());
+        EXPECT_EQ("2001:db8:1::dead:beef", opt.getAddress().toText());
+        EXPECT_EQ(1000, opt.getPreferred());
+        EXPECT_EQ(3000000000U, opt.getValid());
+        EXPECT_EQ(77, opt.getLength());
+
+        // 4 bytes header + 25 bytes content
+        EXPECT_EQ(Option::OPTION6_HDR_LEN + Option6IAPrefix::OPTION6_IAPREFIX_LEN,
+                  opt.len());
+    }
+
+    /// @brief Checks whether content of output buffer is correct
+    ///
+    /// Output buffer is expected to be filled with an option matchin
+    /// buf_ content as defined in setExampleBuffer().
+    ///
+    /// @param expected_type expected option type
+    void checkOutputBuffer(uint16_t expected_type) {
+        // Check if pack worked properly:
+        const uint8_t* out = (const uint8_t*)outBuf_.getData();
+
+        // - if option type is correct
+        EXPECT_EQ(expected_type, out[0]*256 + out[1]);
+
+        // - if option length is correct
+        EXPECT_EQ(25, out[2]*256 + out[3]);
+
+        // - if option content is correct
+        EXPECT_EQ(0, memcmp(out + 4, &buf_[0], 25));
+    }
+
+    OptionBuffer buf_;
+    OutputBuffer outBuf_;
+};
+
+// Tests if receiving option can be parsed correctly
+TEST_F(Option6IAPrefixTest, basic) {
+
+    setExampleBuffer();
+
+    // Create an option (unpack content)
+    boost::scoped_ptr<Option6IAPrefix> opt;
+    ASSERT_NO_THROW(opt.reset(new Option6IAPrefix(D6O_IAPREFIX, buf_.begin(),
+                                                  buf_.begin() + 25)));
+    ASSERT_TRUE(opt);
+
+    // Pack this option
+    opt->pack(outBuf_);
+    EXPECT_EQ(29, outBuf_.getLength());
+
+    checkOption(*opt, D6O_IAPREFIX);
+
+    checkOutputBuffer(D6O_IAPREFIX);
+
+    // Check that option can be disposed safely
+    EXPECT_NO_THROW(opt.reset());
+}
+
+// Checks whether a new option can be built correctly
+TEST_F(Option6IAPrefixTest, build) {
+
+    boost::scoped_ptr<Option6IAPrefix> opt;
+    setExampleBuffer();
+
+    ASSERT_NO_THROW(opt.reset(new Option6IAPrefix(12345,
+                    IOAddress("2001:db8:1::dead:beef"), 77, 1000, 3000000000)));
+    ASSERT_TRUE(opt);
+
+    checkOption(*opt, 12345);
+
+    // Check if we can build it properly
+    EXPECT_NO_THROW(opt->pack(outBuf_));
+    EXPECT_EQ(29, outBuf_.getLength());
+    checkOutputBuffer(12345);
+
+    // Check that option can be disposed safely
+    EXPECT_NO_THROW(opt.reset());
+}
+
+// Checks negative cases
+TEST_F(Option6IAPrefixTest, negative) {
+
+    // Truncated option (at least 25 bytes is needed)
+    EXPECT_THROW(Option6IAPrefix(D6O_IAPREFIX, buf_.begin(), buf_.begin() + 24),
+                 OutOfRange);
+
+    // Empty option
+    EXPECT_THROW(Option6IAPrefix(D6O_IAPREFIX, buf_.begin(), buf_.begin()),
+                 OutOfRange);
+
+    // This is for IPv6 prefixes only
+    EXPECT_THROW(Option6IAPrefix(12345, IOAddress("192.0.2.1"), 77, 1000, 2000),
+                 BadValue);
+
+    // Prefix length can't be larger than 128
+    EXPECT_THROW(Option6IAPrefix(12345, IOAddress("192.0.2.1"), 255, 1000, 2000),
+                 BadValue);
+}
+
+}