BIND 10 master, updated. 1d28cc4d6ad1cc69ba00cc4e1d6822092a259e63 [master] ChangeLog updated.

BIND 10 source code commits bind10-changes at lists.isc.org
Thu Oct 24 19:50:48 UTC 2013


The branch, master has been updated
       via  1d28cc4d6ad1cc69ba00cc4e1d6822092a259e63 (commit)
       via  6f6251bbd761809634aa470f36480d046b4d2a20 (commit)
       via  ff2b7624b7d1bba8ce38c85018b625aecca937ab (commit)
       via  bc5486ec4a8bffd32dd161d7238177c4a50fc94e (commit)
       via  9dc05967b079038d66d4a79ba607ac553500e23b (commit)
       via  95fb9fb9e6a8a45b74169ece6c0ab8b092b85b4a (commit)
      from  2e283ec70fa4c751835e1afcba65c4554fd78d15 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 1d28cc4d6ad1cc69ba00cc4e1d6822092a259e63
Author: Tomek Mrugalski <tomasz at isc.org>
Date:   Thu Oct 24 21:50:32 2013 +0200

    [master] ChangeLog updated.

commit 6f6251bbd761809634aa470f36480d046b4d2a20
Merge: 2e283ec ff2b762
Author: Tomek Mrugalski <tomasz at isc.org>
Date:   Thu Oct 24 21:48:12 2013 +0200

    Merge branch 'trac3201'

-----------------------------------------------------------------------

Summary of changes:
 ChangeLog                                        |    5 +
 doc/guide/bind10-guide.xml                       |    4 +
 src/lib/dhcp/dhcp4.h                             |   23 ++-
 src/lib/dhcp/libdhcp++.cc                        |   22 ++-
 src/lib/dhcp/libdhcp++.h                         |    6 +
 src/lib/dhcp/option.cc                           |    4 +-
 src/lib/dhcp/option_custom.cc                    |   17 ++
 src/lib/dhcp/option_definition.cc                |    6 +-
 src/lib/dhcp/pkt4.cc                             |    2 +-
 src/lib/dhcp/pkt6.cc                             |    4 +-
 src/lib/dhcp/tests/libdhcp++_unittest.cc         |  198 ++++++++++++++++++++--
 src/lib/dhcp/tests/option_custom_unittest.cc     |  198 +++++++++++++++++++---
 src/lib/dhcp/tests/option_definition_unittest.cc |   45 ++++-
 src/lib/dhcp/tests/option_unittest.cc            |    6 +-
 src/lib/dhcp/tests/pkt4_unittest.cc              |    6 +-
 src/lib/dhcp/tests/pkt6_unittest.cc              |    6 +-
 16 files changed, 497 insertions(+), 55 deletions(-)

-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index 4944bda..c4ad8ab 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+699.	[bug]		marcin
+	libdhcp++: Options with defined suboptions are now handled properly.
+	In particular, Relay Agent Info options is now echoed back properly.
+	(Trac #3102, git 6f6251bbd761809634aa470f36480d046b4d2a20)
+
 698.	[bug]		muks
 	A bug was fixed in the interaction between b10-init and b10-msgq
 	that caused BIND 10 failures after repeated start/stop of
diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml
index bc7cc35..6d52f42 100644
--- a/doc/guide/bind10-guide.xml
+++ b/doc/guide/bind10-guide.xml
@@ -4429,6 +4429,10 @@ Dhcp4/subnet4	[]	list	(default)
             Domain Name (15), DNS Servers (6), IP Address Lease Time
             (51), Subnet mask (1), and Routers (3).</simpara>
           </listitem>
+          <listitem>
+            <simpara><ulink url="http://tools.ietf.org/html/rfc3046">RFC 3046</ulink>:
+            Relay Agent Information option is supported.</simpara>
+          </listitem>
       </itemizedlist>
     </section>
 
diff --git a/src/lib/dhcp/dhcp4.h b/src/lib/dhcp/dhcp4.h
index 2bce3a8..392478a 100644
--- a/src/lib/dhcp/dhcp4.h
+++ b/src/lib/dhcp/dhcp4.h
@@ -164,16 +164,27 @@ static const uint16_t DHCP4_SERVER_PORT = 67;
 /// extensions field).
 static const uint32_t DHCP_OPTIONS_COOKIE = 0x63825363;
 
+/* Relay Agent Information option subtypes: */
+
+static const uint16_t RAI_OPTION_AGENT_CIRCUIT_ID = 1; // RFC3046
+static const uint16_t RAI_OPTION_REMOTE_ID = 2; // RFC3046
+/* option 3 is reserved and will never be assigned */
+static const uint16_t RAI_OPTION_DOCSIS_DEVICE_CLASS = 4; // RFC3256
+static const uint16_t RAI_OPTION_LINK_SELECTION = 5; // RFC3527
+static const uint16_t RAI_OPTION_SUBSCRIBER_ID = 6; //RFC3993
+static const uint16_t RAI_OPTION_RADIUS = 7; //RFC4014
+static const uint16_t RAI_OPTION_AUTH = 8; //RFC4030
+static const uint16_t RAI_OPTION_VSI = 9; // RFC4243
+static const uint16_t RAI_OPTION_RELAY_FLAGS = 10; // RFC5010
+static const uint16_t RAI_OPTION_SERVER_ID_OVERRIDE = 11; // RFC5107
+static const uint16_t RAI_OPTION_RELAY_ID = 12; //RFC6925
+static const uint16_t RAI_OPTION_VIRTUAL_SUBNET_SELECT = 151; //RFC6607
+static const uint16_t RAI_OPTION_VIRTUAL_SUBNET_SELECT_CTRL = 152; //RFC6607
+
 // TODO: Following are leftovers from dhcp.h import from ISC DHCP
 // They will be converted to C++-style defines once they will start
 // to be used.
 #if 0
-/* Relay Agent Information option subtypes: */
-#define RAI_CIRCUIT_ID  1
-#define RAI_REMOTE_ID   2
-#define RAI_AGENT_ID    3
-#define RAI_LINK_SELECT 5
-
 /* FQDN suboptions: */
 #define FQDN_NO_CLIENT_UPDATE           1
 #define FQDN_SERVER_UPDATE              2
diff --git a/src/lib/dhcp/libdhcp++.cc b/src/lib/dhcp/libdhcp++.cc
index 351fe8c..b1ebfcc 100644
--- a/src/lib/dhcp/libdhcp++.cc
+++ b/src/lib/dhcp/libdhcp++.cc
@@ -128,14 +128,22 @@ LibDHCP::optionFactory(Option::Universe u,
 
 
 size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
+                               const std::string& option_space,
                                isc::dhcp::OptionCollection& options,
                                size_t* relay_msg_offset /* = 0 */,
                                size_t* relay_msg_len /* = 0 */) {
     size_t offset = 0;
     size_t length = buf.size();
 
-    // Get the list of stdandard option definitions.
-    const OptionDefContainer& option_defs = LibDHCP::getOptionDefs(Option::V6);
+    // Get the list of standard option definitions.
+    OptionDefContainer option_defs;
+    if (option_space == "dhcp6") {
+        option_defs = LibDHCP::getOptionDefs(Option::V6);
+    }
+    // @todo Once we implement other option spaces we should add else clause
+    // here and gather option definitions for them. For now leaving option_defs
+    // empty will imply creation of generic Option.
+
     // Get the search index #1. It allows to search for option definitions
     // using option code.
     const OptionDefContainerTypeIndex& idx = option_defs.get<1>();
@@ -206,11 +214,19 @@ size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
 }
 
 size_t LibDHCP::unpackOptions4(const OptionBuffer& buf,
+                               const std::string& option_space,
                                isc::dhcp::OptionCollection& options) {
     size_t offset = 0;
 
     // Get the list of stdandard option definitions.
-    const OptionDefContainer& option_defs = LibDHCP::getOptionDefs(Option::V4);
+    OptionDefContainer option_defs;
+    if (option_space == "dhcp4") {
+        option_defs = LibDHCP::getOptionDefs(Option::V4);
+    }
+    // @todo Once we implement other option spaces we should add else clause
+    // here and gather option definitions for them. For now leaving option_defs
+    // empty will imply creation of generic Option.
+
     // Get the search index #1. It allows to search for option definitions
     // using option code.
     const OptionDefContainerTypeIndex& idx = option_defs.get<1>();
diff --git a/src/lib/dhcp/libdhcp++.h b/src/lib/dhcp/libdhcp++.h
index 71cf3f4..045b05a 100644
--- a/src/lib/dhcp/libdhcp++.h
+++ b/src/lib/dhcp/libdhcp++.h
@@ -108,9 +108,12 @@ public:
     /// in options container.
     ///
     /// @param buf Buffer to be parsed.
+    /// @param option_space A name of the option space which holds definitions
+    /// of to be used to parse options in the packets.
     /// @param options Reference to option container. Options will be
     ///        put here.
     static size_t unpackOptions4(const OptionBuffer& buf,
+                                 const std::string& option_space,
                                  isc::dhcp::OptionCollection& options);
 
     /// @brief Parses provided buffer as DHCPv6 options and creates Option objects.
@@ -125,6 +128,8 @@ public:
     /// iteration its content will be treated as buffer to be parsed.
     ///
     /// @param buf Buffer to be parsed.
+    /// @param option_space A name of the option space which holds definitions
+    /// of to be used to parse options in the packets.
     /// @param options Reference to option container. Options will be
     ///        put here.
     /// @param relay_msg_offset reference to a size_t structure. If specified,
@@ -133,6 +138,7 @@ public:
     ///        length of the relay_msg option will be stored in it.
     /// @return offset to the first byte after last parsed option
     static size_t unpackOptions6(const OptionBuffer& buf,
+                                 const std::string& option_space,
                                  isc::dhcp::OptionCollection& options,
                                  size_t* relay_msg_offset = 0,
                                  size_t* relay_msg_len = 0);
diff --git a/src/lib/dhcp/option.cc b/src/lib/dhcp/option.cc
index 46889e0..f5ab75e 100644
--- a/src/lib/dhcp/option.cc
+++ b/src/lib/dhcp/option.cc
@@ -135,10 +135,10 @@ Option::unpackOptions(const OptionBuffer& buf) {
 
     switch (universe_) {
     case V4:
-        LibDHCP::unpackOptions4(buf, options_);
+        LibDHCP::unpackOptions4(buf, getEncapsulatedSpace(), options_);
         return;
     case V6:
-        LibDHCP::unpackOptions6(buf, options_);
+        LibDHCP::unpackOptions6(buf, getEncapsulatedSpace(), options_);
         return;
     default:
         isc_throw(isc::BadValue, "Invalid universe type " << universe_);
diff --git a/src/lib/dhcp/option_custom.cc b/src/lib/dhcp/option_custom.cc
index 0cca9b3..278c0c9 100644
--- a/src/lib/dhcp/option_custom.cc
+++ b/src/lib/dhcp/option_custom.cc
@@ -249,6 +249,12 @@ OptionCustom::createBuffers(const OptionBuffer& data_buf) {
             // Proceed to the next data field.
             data += data_size;
         }
+
+        // Unpack suboptions if any.
+        if (data != data_buf.end() && !getEncapsulatedSpace().empty()) {
+            unpackOptions(OptionBuffer(data, data_buf.end()));
+        }
+
     } else if (data_type != OPT_EMPTY_TYPE) {
         // If data_type value is other than OPT_RECORD_TYPE, our option is
         // empty (have no data at all) or it comprises one or more
@@ -316,9 +322,20 @@ OptionCustom::createBuffers(const OptionBuffer& data_buf) {
             }
             if (data_size > 0) {
                 buffers.push_back(OptionBuffer(data, data + data_size));
+                data += data_size;
             } else {
                 isc_throw(OutOfRange, "option buffer truncated");
             }
+
+            // Unpack suboptions if any.
+            if (data != data_buf.end() && !getEncapsulatedSpace().empty()) {
+                unpackOptions(OptionBuffer(data, data_buf.end()));
+            }
+        }
+    } else if (data_type == OPT_EMPTY_TYPE) {
+        // Unpack suboptions if any.
+        if (data != data_buf.end() && !getEncapsulatedSpace().empty()) {
+            unpackOptions(OptionBuffer(data, data_buf.end()));
         }
     }
     // If everything went ok we can replace old buffer set with new ones.
diff --git a/src/lib/dhcp/option_definition.cc b/src/lib/dhcp/option_definition.cc
index 8c1cad0..c75c6f9 100644
--- a/src/lib/dhcp/option_definition.cc
+++ b/src/lib/dhcp/option_definition.cc
@@ -118,7 +118,11 @@ OptionDefinition::optionFactory(Option::Universe u, uint16_t type,
     try {
         switch(type_) {
         case OPT_EMPTY_TYPE:
-            return (factoryEmpty(u, type));
+            if (getEncapsulatedSpace().empty()) {
+                    return (factoryEmpty(u, type));
+            } else {
+                return (OptionPtr(new OptionCustom(*this, u, begin, end)));
+            }
 
         case OPT_BINARY_TYPE:
             return (factoryGeneric(u, type, begin, end));
diff --git a/src/lib/dhcp/pkt4.cc b/src/lib/dhcp/pkt4.cc
index 468037a..8a5b8ab 100644
--- a/src/lib/dhcp/pkt4.cc
+++ b/src/lib/dhcp/pkt4.cc
@@ -219,7 +219,7 @@ Pkt4::unpack() {
     // a vector as an input.
     buffer_in.readVector(opts_buffer, opts_len);
     if (callback_.empty()) {
-        LibDHCP::unpackOptions4(opts_buffer, options_);
+        LibDHCP::unpackOptions4(opts_buffer, "dhcp4", options_);
     } else {
         // The last two arguments are set to NULL because they are
         // specific to DHCPv6 options parsing. They are unused for
diff --git a/src/lib/dhcp/pkt6.cc b/src/lib/dhcp/pkt6.cc
index 57daee0..308880e 100644
--- a/src/lib/dhcp/pkt6.cc
+++ b/src/lib/dhcp/pkt6.cc
@@ -330,7 +330,7 @@ Pkt6::unpackMsg(OptionBuffer::const_iterator begin,
         // If custom option parsing function has been set, use this function
         // to parse options. Otherwise, use standard function from libdhcp.
         if (callback_.empty()) {
-            LibDHCP::unpackOptions6(opt_buffer, options_);
+            LibDHCP::unpackOptions6(opt_buffer, "dhcp6", options_);
         } else {
             // The last two arguments hold the DHCPv6 Relay message offset and
             // length. Setting them to NULL because we are dealing with the
@@ -377,7 +377,7 @@ Pkt6::unpackRelayMsg() {
             // If custom option parsing function has been set, use this function
             // to parse options. Otherwise, use standard function from libdhcp.
             if (callback_.empty()) {
-                LibDHCP::unpackOptions6(opt_buffer, relay.options_,
+                LibDHCP::unpackOptions6(opt_buffer, "dhcp6", relay.options_,
                                         &relay_msg_offset, &relay_msg_len);
             } else {
                 callback_(opt_buffer, "dhcp6", relay.options_,
diff --git a/src/lib/dhcp/tests/libdhcp++_unittest.cc b/src/lib/dhcp/tests/libdhcp++_unittest.cc
index 776b084..614caff 100644
--- a/src/lib/dhcp/tests/libdhcp++_unittest.cc
+++ b/src/lib/dhcp/tests/libdhcp++_unittest.cc
@@ -42,6 +42,12 @@ using namespace isc::dhcp;
 using namespace isc::util;
 
 namespace {
+
+// DHCPv6 suboptions of Vendor Options Option.
+/// @todo move to src/lib/dhcp/docsis3_option_defs.h once #3194 is merged.
+const uint16_t OPTION_CMTS_CAPS = 1025;
+const uint16_t OPTION_CM_MAC = 1026;
+
 class LibDhcpTest : public ::testing::Test {
 public:
     LibDhcpTest() { }
@@ -105,6 +111,33 @@ public:
         testStdOptionDefs(Option::V6, code, begin, end, expected_type,
                           encapsulates);
     }
+
+    /// @brief Create a sample DHCPv4 option 43 with suboptions.
+    static OptionBuffer createVendorOption() {
+        const uint8_t opt_data[] = {
+            0x2B, 0x0D,  // Vendor-Specific Information (CableLabs)
+            // Suboptions start here...
+            0x02, 0x05,  // Device Type Option (length = 5)
+            'D', 'u', 'm', 'm', 'y',
+            0x04, 0x04,   // Serial Number Option (length = 4)
+            0x42, 0x52, 0x32, 0x32 // Serial number
+        };
+        return (OptionBuffer(opt_data, opt_data + sizeof(opt_data)));
+    }
+
+    /// @brief Create a sample DHCPv4 option 82 with suboptions.
+    static OptionBuffer createAgentInformationOption() {
+        const uint8_t opt_data[] = {
+            0x52, 0x0E, // Agent Information Option (length = 14)
+            // Suboptions start here...
+            0x01, 0x04, // Agent Circuit ID (length = 4)
+            0x20, 0x00, 0x00, 0x02, // ID
+            0x02, 0x06, // Agent Remote ID
+            0x20, 0xE5, 0x2A, 0xB8, 0x15, 0x14 // ID
+        };
+        return (OptionBuffer(opt_data, opt_data + sizeof(opt_data)));
+    }
+
 private:
 
     /// @brief Test DHCPv4 or DHCPv6 option definition.
@@ -177,7 +210,19 @@ const uint8_t v6packed[] = {
     0, 2, 0, 3, 105, 106, 107, // SERVER_ID (7 bytes)
     0, 14, 0, 0, // RAPID_COMMIT (0 bytes)
     0,  6, 0, 4, 108, 109, 110, 111, // ORO (8 bytes)
-    0,  8, 0, 2, 112, 113 // ELAPSED_TIME (6 bytes)
+    0,  8, 0, 2, 112, 113, // ELAPSED_TIME (6 bytes)
+    // Vendor Specific Information Option starts here
+    0x00, 0x11,  // VSI Option Code
+    0x00, 0x16,  // VSI Option Length
+    0x00, 0x00, 0x11, 0x8B, // Enterprise ID
+    0x04, 0x01,  // CMTS Capabilities Option
+    0x00, 0x04,  // Length
+    0x01, 0x02,
+    0x03, 0x00,  // DOCSIS Version Number
+    0x04, 0x02,  // CM MAC Address Suboption
+    0x00, 0x06,  // Length
+    0x74, 0x56, 0x12, 0x29, 0x97, 0xD0, // Actual MAC Address
+
 };
 
 TEST_F(LibDhcpTest, optionFactory) {
@@ -267,11 +312,23 @@ TEST_F(LibDhcpTest, packOptions6) {
     OptionPtr opt4(new Option(Option::V6, 6, buf.begin() + 8, buf.begin() + 12));
     OptionPtr opt5(new Option(Option::V6, 8, buf.begin() + 12, buf.begin() + 14));
 
+    OptionPtr cm_mac(new Option(Option::V6, OPTION_CM_MAC,
+                                OptionBuffer(v6packed + 54, v6packed  + 60)));
+
+    OptionPtr cmts_caps(new Option(Option::V6, OPTION_CMTS_CAPS,
+                                   OptionBuffer(v6packed + 46, v6packed + 50)));
+
+    boost::shared_ptr<OptionInt<uint32_t> >
+        vsi(new OptionInt<uint32_t>(Option::V6, D6O_VENDOR_OPTS, 4491));
+    vsi->addOption(cm_mac);
+    vsi->addOption(cmts_caps);
+
     opts.insert(make_pair(opt1->getType(), opt1));
     opts.insert(make_pair(opt1->getType(), opt2));
     opts.insert(make_pair(opt1->getType(), opt3));
     opts.insert(make_pair(opt1->getType(), opt4));
     opts.insert(make_pair(opt1->getType(), opt5));
+    opts.insert(make_pair(opt1->getType(), vsi));
 
     OutputBuffer assembled(512);
 
@@ -293,10 +350,10 @@ TEST_F(LibDhcpTest, unpackOptions6) {
 
     EXPECT_NO_THROW ({
             LibDHCP::unpackOptions6(OptionBuffer(buf.begin(), buf.begin() + sizeof(v6packed)),
-                                    options);
+                                    "dhcp6", options);
     });
 
-    EXPECT_EQ(options.size(), 5); // there should be 5 options
+    EXPECT_EQ(options.size(), 6); // there should be 5 options
 
     isc::dhcp::OptionCollection::const_iterator x = options.find(1);
     ASSERT_FALSE(x == options.end()); // option 1 should exist
@@ -357,6 +414,27 @@ TEST_F(LibDhcpTest, unpackOptions6) {
     // Returned value should be equivalent to two byte values: 112, 113
     EXPECT_EQ(0x7071, opt_elapsed_time->getValue());
 
+    // Check if Vendor Specific Information Option along with suboptions
+    // have been parsed correctly.
+    x = options.find(D6O_VENDOR_OPTS);
+    EXPECT_FALSE(x == options.end());
+    EXPECT_EQ(D6O_VENDOR_OPTS, x->second->getType());
+    EXPECT_EQ(26, x->second->len());
+
+    // CM MAC Address Option
+    OptionPtr cm_mac = x->second->getOption(OPTION_CM_MAC);
+    ASSERT_TRUE(cm_mac);
+    EXPECT_EQ(OPTION_CM_MAC, cm_mac->getType());
+    ASSERT_EQ(10, cm_mac->len());
+    EXPECT_EQ(0, memcmp(&cm_mac->getData()[0], v6packed + 54, 6));
+
+    // CMTS Capabilities
+    OptionPtr cmts_caps = x->second->getOption(OPTION_CMTS_CAPS);
+    ASSERT_TRUE(cmts_caps);
+    EXPECT_EQ(OPTION_CMTS_CAPS, cmts_caps->getType());
+    ASSERT_EQ(8, cmts_caps->len());
+    EXPECT_EQ(0, memcmp(&cmts_caps->getData()[0], v6packed + 46, 4));
+
     x = options.find(0);
     EXPECT_TRUE(x == options.end()); // option 0 not found
 
@@ -380,7 +458,12 @@ static uint8_t v4_opts[] = {
     60,  3, 10, 11, 12, // Class Id
     14,  3, 20, 21, 22, // Merit Dump File
     254, 3, 30, 31, 32, // Reserved
-    128, 3, 40, 41, 42  // Vendor specific
+    128, 3, 40, 41, 42, // Vendor specific
+    0x52, 0x19,         // RAI
+    0x01, 0x04, 0x20, 0x00, 0x00, 0x02, // Agent Circuit ID
+    0x02, 0x06, 0x20, 0xE5, 0x2A, 0xB8, 0x15, 0x14, // Agent Remote ID
+    0x09, 0x09, 0x00, 0x00, 0x11, 0x8B, 0x04, // Vendor Specific Information
+    0x01, 0x02, 0x03, 0x00  // Vendor Specific Information continued
 };
 
 TEST_F(LibDhcpTest, packOptions4) {
@@ -399,20 +482,53 @@ TEST_F(LibDhcpTest, packOptions4) {
     OptionPtr opt4(new Option(Option::V4,254, payload[3]));
     OptionPtr opt5(new Option(Option::V4,128, payload[4]));
 
+    // Add RAI option, which comprises 3 sub-options.
+
+    // Get the option definition for RAI option. This option is represented
+    // by OptionCustom which requires a definition to be passed to
+    // the constructor.
+    OptionDefinitionPtr rai_def = LibDHCP::getOptionDef(Option::V4,
+                                                        DHO_DHCP_AGENT_OPTIONS);
+    ASSERT_TRUE(rai_def);
+    // Create RAI option.
+    OptionCustomPtr rai(new OptionCustom(*rai_def, Option::V4));
+
+    // The sub-options are created using the bits of v4_opts buffer because
+    // we want to use this buffer as a reference to verify that produced
+    // option in on-wire format is correct.
+
+    // Create Ciruit ID sub-option and add to RAI.
+    OptionPtr circuit_id(new Option(Option::V4, RAI_OPTION_AGENT_CIRCUIT_ID,
+                                    OptionBuffer(v4_opts + 29,
+                                                 v4_opts + 33)));
+    rai->addOption(circuit_id);
+
+    // Create Remote ID option and add to RAI.
+    OptionPtr remote_id(new Option(Option::V4, RAI_OPTION_REMOTE_ID,
+                                   OptionBuffer(v4_opts + 35, v4_opts + 41)));
+    rai->addOption(remote_id);
+
+    // Create Vendor Specific Information and add to RAI.
+    OptionPtr vsi(new Option(Option::V4, RAI_OPTION_VSI,
+                             OptionBuffer(v4_opts + 43, v4_opts + 52)));
+    rai->addOption(vsi);
+
     isc::dhcp::OptionCollection opts; // list of options
+    // Note that we insert each option under the same option code into
+    // the map. This gurantees that options are packed in the same order
+    // they were added. Otherwise, options would get sorted by code and
+    // the resulting buffer wouldn't match with the reference buffer.
     opts.insert(make_pair(opt1->getType(), opt1));
     opts.insert(make_pair(opt1->getType(), opt2));
     opts.insert(make_pair(opt1->getType(), opt3));
     opts.insert(make_pair(opt1->getType(), opt4));
     opts.insert(make_pair(opt1->getType(), opt5));
-
-    vector<uint8_t> expVect(v4_opts, v4_opts + sizeof(v4_opts));
+    opts.insert(make_pair(opt1->getType(), rai));
 
     OutputBuffer buf(100);
     EXPECT_NO_THROW(LibDHCP::packOptions(buf, opts));
     ASSERT_EQ(buf.getLength(), sizeof(v4_opts));
     EXPECT_EQ(0, memcmp(v4_opts, buf.getData(), sizeof(v4_opts)));
-
 }
 
 TEST_F(LibDhcpTest, unpackOptions4) {
@@ -421,7 +537,7 @@ TEST_F(LibDhcpTest, unpackOptions4) {
     isc::dhcp::OptionCollection options; // list of options
 
     ASSERT_NO_THROW(
-        LibDHCP::unpackOptions4(v4packed, options);
+        LibDHCP::unpackOptions4(v4packed, "dhcp4", options);
     );
 
     isc::dhcp::OptionCollection::const_iterator x = options.find(12);
@@ -464,6 +580,48 @@ TEST_F(LibDhcpTest, unpackOptions4) {
     EXPECT_EQ(5, x->second->len()); // total option length 5
     EXPECT_EQ(0, memcmp(&x->second->getData()[0], v4_opts + 22, 3)); // data len=3
 
+    // Checking DHCP Relay Agent Information Option.
+    x = options.find(DHO_DHCP_AGENT_OPTIONS);
+    ASSERT_FALSE(x == options.end());
+    EXPECT_EQ(DHO_DHCP_AGENT_OPTIONS, x->second->getType());
+    // RAI is represented by OptionCustom.
+    OptionCustomPtr rai = boost::dynamic_pointer_cast<OptionCustom>(x->second);
+    ASSERT_TRUE(rai);
+    // RAI should have 3 sub-options: Circuit ID, Agent Remote ID, Vendor
+    // Specific Information option. Note that by parsing these suboptions we
+    // are checking that unpackOptions4 differentiates between standard option
+    // space called "dhcp4" and other option spaces. These sub-options do not
+    // belong to standard option space and should be parsed using different
+    // option definitions.
+    // @todo Currently, definitions for option space "dhcp-agent-options-space"
+    // are not defined. Therefore all suboptions will be represented here by
+    // the generic Option class.
+
+    // Check that Circuit ID option is among parsed options.
+    OptionPtr rai_option = rai->getOption(RAI_OPTION_AGENT_CIRCUIT_ID);
+    ASSERT_TRUE(rai_option);
+    EXPECT_EQ(RAI_OPTION_AGENT_CIRCUIT_ID, rai_option->getType());
+    ASSERT_EQ(6, rai_option->len());
+    EXPECT_EQ(0, memcmp(&rai_option->getData()[0], v4_opts + 29, 4));
+
+    // Check that Remote ID option is among parsed options.
+    rai_option = rai->getOption(RAI_OPTION_REMOTE_ID);
+    ASSERT_TRUE(rai_option);
+    EXPECT_EQ(RAI_OPTION_REMOTE_ID, rai_option->getType());
+    ASSERT_EQ(8, rai_option->len());
+    EXPECT_EQ(0, memcmp(&rai_option->getData()[0], v4_opts + 35, 6));
+
+    // Check that Vendor Specific Information option is among parsed options.
+    rai_option = rai->getOption(RAI_OPTION_VSI);
+    ASSERT_TRUE(rai_option);
+    EXPECT_EQ(RAI_OPTION_VSI, rai_option->getType());
+    ASSERT_EQ(11, rai_option->len());
+    EXPECT_EQ(0, memcmp(&rai_option->getData()[0], v4_opts + 43, 11));
+
+    // Make sure, that option other than those above is not present.
+    EXPECT_FALSE(rai->getOption(10));
+
+    // Check the same for the global option space.
     x = options.find(0);
     EXPECT_TRUE(x == options.end()); // option 0 not found
 
@@ -472,6 +630,7 @@ TEST_F(LibDhcpTest, unpackOptions4) {
 
     x = options.find(2);
     EXPECT_TRUE(x == options.end()); // option 2 not found
+
 }
 
 TEST_F(LibDhcpTest, isStandardOption4) {
@@ -653,8 +812,8 @@ TEST_F(LibDhcpTest, stdOptionDefs4) {
     LibDhcpTest::testStdOptionDefs4(DHO_DEFAULT_TCP_TTL, begin, begin + 1,
                                     typeid(OptionInt<uint8_t>));
 
-    LibDhcpTest::testStdOptionDefs4(DHO_TCP_KEEPALIVE_INTERVAL, begin, begin + 4,
-                                    typeid(OptionInt<uint32_t>));
+    LibDhcpTest::testStdOptionDefs4(DHO_TCP_KEEPALIVE_INTERVAL, begin,
+                                    begin + 4, typeid(OptionInt<uint32_t>));
 
     LibDhcpTest::testStdOptionDefs4(DHO_TCP_KEEPALIVE_GARBAGE, begin, begin + 1,
                                     typeid(OptionCustom));
@@ -668,8 +827,13 @@ TEST_F(LibDhcpTest, stdOptionDefs4) {
     LibDhcpTest::testStdOptionDefs4(DHO_NTP_SERVERS, begin, end,
                                     typeid(Option4AddrLst));
 
-    LibDhcpTest::testStdOptionDefs4(DHO_VENDOR_ENCAPSULATED_OPTIONS, begin, end,
-                                    typeid(Option),
+    // The following option requires well formed buffer to be created from.
+    // Not just a dummy one. This buffer includes some suboptions.
+    OptionBuffer vendor_opts_buf = createVendorOption();
+    LibDhcpTest::testStdOptionDefs4(DHO_VENDOR_ENCAPSULATED_OPTIONS,
+                                    vendor_opts_buf.begin(),
+                                    vendor_opts_buf.end(),
+                                    typeid(OptionCustom),
                                     "vendor-encapsulated-options-space");
 
     LibDhcpTest::testStdOptionDefs4(DHO_NETBIOS_NAME_SERVERS, begin, end,
@@ -744,8 +908,14 @@ TEST_F(LibDhcpTest, stdOptionDefs4) {
     LibDhcpTest::testStdOptionDefs4(DHO_FQDN, begin, begin + 3,
                                     typeid(Option4ClientFqdn));
 
-    LibDhcpTest::testStdOptionDefs4(DHO_DHCP_AGENT_OPTIONS, begin, end,
-                                    typeid(Option), "dhcp-agent-options-space");
+    // The following option requires well formed buffer to be created from.
+    // Not just a dummy one. This buffer includes some suboptions.
+    OptionBuffer agent_info_buf = createAgentInformationOption();
+    LibDhcpTest::testStdOptionDefs4(DHO_DHCP_AGENT_OPTIONS,
+                                    agent_info_buf.begin(),
+                                    agent_info_buf.end(),
+                                    typeid(OptionCustom),
+                                    "dhcp-agent-options-space");
 
     LibDhcpTest::testStdOptionDefs4(DHO_AUTHENTICATE, begin, end,
                                     typeid(Option));
diff --git a/src/lib/dhcp/tests/option_custom_unittest.cc b/src/lib/dhcp/tests/option_custom_unittest.cc
index c04bbc2..4add2d8 100644
--- a/src/lib/dhcp/tests/option_custom_unittest.cc
+++ b/src/lib/dhcp/tests/option_custom_unittest.cc
@@ -32,6 +32,59 @@ public:
     /// @brief Constructor.
     OptionCustomTest() { }
 
+    /// @brief Appends DHCPv4 suboption in the on-wire format to the buffer.
+    ///
+    /// @param buf A buffer to which suboption is appended.
+    void appendV4Suboption(OptionBuffer& buf) {
+        const uint8_t subopt_data[] = {
+            0x01, 0x02, // Option type = 1, length = 2
+            0x01, 0x02  // Two bytes of data
+        };
+        buf.insert(buf.end(), subopt_data, subopt_data + sizeof(subopt_data));
+    }
+
+    /// @brief Check if the parsed option has a suboption.
+    ///
+    /// @param opt An option in which suboption is expected.
+    /// @return Assertion result indicating that the suboption is
+    /// present (success) or missing (failure).
+    ::testing::AssertionResult hasV4Suboption(OptionCustom* opt) {
+        OptionPtr subopt = opt->getOption(1);
+        if (!subopt) {
+            return (::testing::AssertionFailure(::testing::Message()
+                                                << "Suboption of OptionCustom"
+                                                " is missing"));
+        }
+        return (::testing::AssertionSuccess());
+    }
+
+    /// @brief Appends DHCPv6 suboption in the on-wire format to the buffer.
+    ///
+    /// @param buf A buffer to which suboption is appended.
+    void appendV6Suboption(OptionBuffer& buf) {
+        const uint8_t subopt_data[] = {
+            0x00, 0x01, // Option type = 1
+            0x00, 0x04, // Option length = 4
+            0x01, 0x02, 0x03, 0x04 // Four bytes of data
+        };
+        buf.insert(buf.end(), subopt_data, subopt_data + sizeof(subopt_data));
+    }
+
+    /// @brief Check if the parsed option has a suboption.
+    ///
+    /// @param opt An option in which suboption is expected.
+    /// @return Assertion result indicating that the suboption is
+    /// present (success) or missing (failure).
+    ::testing::AssertionResult hasV6Suboption(OptionCustom* opt) {
+        OptionPtr subopt = opt->getOption(1);
+        if (!subopt) {
+            return (::testing::AssertionFailure(::testing::Message()
+                                                << "Suboption of OptionCustom"
+                                                " is missing"));
+        }
+        return (::testing::AssertionSuccess());
+    }
+
     /// @brief Write IP address into a buffer.
     ///
     /// @param address address to be written.
@@ -114,23 +167,30 @@ TEST_F(OptionCustomTest, constructor) {
 // The purpose of this test is to verify that 'empty' option definition can
 // be used to create an instance of custom option.
 TEST_F(OptionCustomTest, emptyData) {
-    OptionDefinition opt_def("OPTION_FOO", 232, "empty");
+    OptionDefinition opt_def("option-foo", 232, "empty", "option-foo-space");
 
+    // Create a buffer holding 1 suboption.
     OptionBuffer buf;
+    appendV4Suboption(buf);
+
     boost::scoped_ptr<OptionCustom> option;
     ASSERT_NO_THROW(
-        option.reset(new OptionCustom(opt_def, Option::V4, buf.begin(), buf.end()));
+        option.reset(new OptionCustom(opt_def, Option::V4, buf.begin(),
+                                      buf.end()));
     );
     ASSERT_TRUE(option);
 
     // Option is 'empty' so no data fields are expected.
     EXPECT_EQ(0, option->getDataFieldsNum());
+
+    // Check that suboption has been parsed.
+    EXPECT_TRUE(hasV4Suboption(option.get()));
 }
 
 // The purpose of this test is to verify that the option definition comprising
 // a binary value can be used to create an instance of custom option.
 TEST_F(OptionCustomTest, binaryData) {
-    OptionDefinition opt_def("OPTION_FOO", 231, "binary");
+    OptionDefinition opt_def("option-foo", 231, "binary", "option-foo-space");
 
     // Create a buffer holding some binary data. This data will be
     // used as reference when we read back the data from a created
@@ -139,6 +199,11 @@ TEST_F(OptionCustomTest, binaryData) {
     for (int i = 0; i < 14; ++i) {
         buf_in[i] = i;
     }
+
+    // Append suboption data. This data should NOT be recognized when
+    // option has a binary format.
+    appendV4Suboption(buf_in);
+
     // Use scoped pointer because it allows to declare the option
     // in the function scope and initialize it under ASSERT.
     boost::scoped_ptr<OptionCustom> option;
@@ -169,20 +234,24 @@ TEST_F(OptionCustomTest, binaryData) {
                                       buf_in.end())),
         isc::OutOfRange
     );
+
+    // Suboptions are not recognized for the binary formats because as it is
+    // a variable length format. Therefore, we expect that there are no
+    // suboptions in the parsed option.
+    EXPECT_FALSE(option->getOption(1));
 }
 
 // The purpose of this test is to verify that an option definition comprising
 // a single boolean value can be used to create an instance of custom option.
 TEST_F(OptionCustomTest, booleanData) {
-    OptionDefinition opt_def("OPTION_FOO", 1000, "boolean");
+    OptionDefinition opt_def("option-foo", 1000, "boolean", "option-foo-space");
 
     OptionBuffer buf;
     // Push back the value that represents 'false'.
     buf.push_back(0);
-    // Push back the 'true' value. Note that this value should
-    // be ignored by custom option because it holds single boolean
-    // value (according to option definition).
-    buf.push_back(1);
+
+    // Append suboption. It should be present in the parsed packet.
+    appendV6Suboption(buf);
 
     boost::scoped_ptr<OptionCustom> option;
     ASSERT_NO_THROW(
@@ -202,10 +271,14 @@ TEST_F(OptionCustomTest, booleanData) {
     ASSERT_NO_THROW(value = option->readBoolean(0));
     EXPECT_FALSE(value);
 
+    // There should be one suboption present.
+    EXPECT_TRUE(hasV6Suboption(option.get()));
+
     // Check that the option with "no data" is rejected.
     buf.clear();
     EXPECT_THROW(
-        option.reset(new OptionCustom(opt_def, Option::V6, buf.begin(), buf.end())),
+        option.reset(new OptionCustom(opt_def, Option::V6, buf.begin(),
+                                      buf.end())),
         isc::OutOfRange
     );
 }
@@ -213,7 +286,7 @@ TEST_F(OptionCustomTest, booleanData) {
 // The purpose of this test is to verify that the data from a buffer
 // can be read as FQDN.
 TEST_F(OptionCustomTest, fqdnData) {
-    OptionDefinition opt_def("OPTION_FOO", 1000, "fqdn");
+    OptionDefinition opt_def("option-foo", 1000, "fqdn", "option-foo-space");
 
     const char data[] = {
         8, 109, 121, 100, 111, 109, 97, 105, 110, // "mydomain"
@@ -224,6 +297,10 @@ TEST_F(OptionCustomTest, fqdnData) {
 
     std::vector<uint8_t> buf(data, data + sizeof(data));
 
+    // The FQDN has a certain boundary. Right after FQDN it should be
+    // possible to append suboption and parse it correctly.
+    appendV6Suboption(buf);
+
     boost::scoped_ptr<OptionCustom> option;
     ASSERT_NO_THROW(
         option.reset(new OptionCustom(opt_def, Option::V6, buf.begin(), buf.end()));
@@ -235,6 +312,9 @@ TEST_F(OptionCustomTest, fqdnData) {
     std::string domain0 = option->readFqdn(0);
     EXPECT_EQ("mydomain.example.com.", domain0);
 
+    // This option should have one suboption.
+    EXPECT_TRUE(hasV6Suboption(option.get()));
+
     // Check that the option with truncated data can't be created.
     EXPECT_THROW(
         option.reset(new OptionCustom(opt_def, Option::V6,
@@ -246,12 +326,15 @@ TEST_F(OptionCustomTest, fqdnData) {
 // The purpose of this test is to verify that the option definition comprising
 // 16-bit signed integer value can be used to create an instance of custom option.
 TEST_F(OptionCustomTest, int16Data) {
-    OptionDefinition opt_def("OPTION_FOO", 1000, "int16");
+    OptionDefinition opt_def("option-foo", 1000, "int16", "option-foo-space");
 
     OptionBuffer buf;
     // Store signed integer value in the input buffer.
     writeInt<int16_t>(-234, buf);
 
+    // Append suboption.
+    appendV6Suboption(buf);
+
     // Create custom option.
     boost::scoped_ptr<OptionCustom> option;
     ASSERT_NO_THROW(
@@ -268,6 +351,9 @@ TEST_F(OptionCustomTest, int16Data) {
     ASSERT_NO_THROW(value = option->readInteger<int16_t>(0));
     EXPECT_EQ(-234, value);
 
+    // Parsed option should have one suboption.
+    EXPECT_TRUE(hasV6Suboption(option.get()));
+
     // Check that the option is not created when a buffer is
     // too short (1 byte instead of 2 bytes).
     EXPECT_THROW(
@@ -279,11 +365,13 @@ TEST_F(OptionCustomTest, int16Data) {
 // The purpose of this test is to verify that the option definition comprising
 // 32-bit signed integer value can be used to create an instance of custom option.
 TEST_F(OptionCustomTest, int32Data) {
-    OptionDefinition opt_def("OPTION_FOO", 1000, "int32");
+    OptionDefinition opt_def("option-foo", 1000, "int32", "option-foo-space");
 
     OptionBuffer buf;
     writeInt<int32_t>(-234, buf);
-    writeInt<int32_t>(100, buf);
+
+    // Append one suboption.
+    appendV6Suboption(buf);
 
     // Create custom option.
     boost::scoped_ptr<OptionCustom> option;
@@ -301,6 +389,9 @@ TEST_F(OptionCustomTest, int32Data) {
     ASSERT_NO_THROW(value = option->readInteger<int32_t>(0));
     EXPECT_EQ(-234, value);
 
+    // The parsed option should have one suboption.
+    EXPECT_TRUE(hasV6Suboption(option.get()));
+
     // Check that the option is not created when a buffer is
     // too short (3 bytes instead of 4 bytes).
     EXPECT_THROW(
@@ -312,12 +403,16 @@ TEST_F(OptionCustomTest, int32Data) {
 // The purpose of this test is to verify that the option definition comprising
 // single IPv4 address can be used to create an instance of custom option.
 TEST_F(OptionCustomTest, ipv4AddressData) {
-    OptionDefinition opt_def("OPTION_FOO", 231, "ipv4-address");
+    OptionDefinition opt_def("OPTION_FOO", 231, "ipv4-address",
+                             "option-foo-space");
 
     // Create input buffer.
     OptionBuffer buf;
     writeAddress(IOAddress("192.168.100.50"), buf);
 
+    // Append one suboption.
+    appendV4Suboption(buf);
+
     // Create custom option.
     boost::scoped_ptr<OptionCustom> option;
     ASSERT_NO_THROW(
@@ -334,6 +429,9 @@ TEST_F(OptionCustomTest, ipv4AddressData) {
 
     EXPECT_EQ("192.168.100.50", address.toText());
 
+    // Parsed option should have one suboption.
+    EXPECT_TRUE(hasV4Suboption(option.get()));
+
     // Check that option is not created if the provided buffer is
     // too short (use 3 bytes instead of 4).
     EXPECT_THROW(
@@ -345,12 +443,16 @@ TEST_F(OptionCustomTest, ipv4AddressData) {
 // The purpose of this test is to verify that the option definition comprising
 // single IPv6 address can be used to create an instance of custom option.
 TEST_F(OptionCustomTest, ipv6AddressData) {
-    OptionDefinition opt_def("OPTION_FOO", 1000, "ipv6-address");
+    OptionDefinition opt_def("option-foo", 1000, "ipv6-address",
+                             "option-foo-space");
 
     // Initialize input buffer.
     OptionBuffer buf;
     writeAddress(IOAddress("2001:db8:1::100"), buf);
 
+    // Append suboption.
+    appendV6Suboption(buf);
+
     // Create custom option using input buffer.
     boost::scoped_ptr<OptionCustom> option;
     ASSERT_NO_THROW(
@@ -369,6 +471,9 @@ TEST_F(OptionCustomTest, ipv6AddressData) {
 
     EXPECT_EQ("2001:db8:1::100", address.toText());
 
+    // Parsed option should have one suboption.
+    EXPECT_TRUE(hasV6Suboption(option.get()));
+
     // Check that option is not created if the provided buffer is
     // too short (use 15 bytes instead of 16).
     EXPECT_THROW(
@@ -382,12 +487,19 @@ TEST_F(OptionCustomTest, ipv6AddressData) {
 // The purpose of this test is to verify that the option definition comprising
 // string value can be used to create an instance of custom option.
 TEST_F(OptionCustomTest, stringData) {
-    OptionDefinition opt_def("OPTION_FOO", 1000, "string");
+    OptionDefinition opt_def("option-foo", 1000, "string", "option-foo-space");
 
     // Create an input buffer holding some string value.
     OptionBuffer buf;
     writeString("hello world!", buf);
 
+    // Append suboption. It should not be detected because the string field
+    //  has variable length.
+    appendV6Suboption(buf);
+
+    // Append suboption. Since the option has variable length string field,
+    // the suboption should not be recognized.
+
     // Create custom option using input buffer.
     boost::scoped_ptr<OptionCustom> option;
     ASSERT_NO_THROW(
@@ -403,7 +515,14 @@ TEST_F(OptionCustomTest, stringData) {
     std::string value;
     ASSERT_NO_THROW(value = option->readString(0));
 
-    EXPECT_EQ("hello world!", value);
+    // The initial part of the string should contain the actual string.
+    // The rest of it is a garbage from an attempt to decode suboption
+    // as a string.
+    ASSERT_EQ(20, value.size());
+    EXPECT_EQ("hello world!", value.substr(0, 12));
+
+    // No suboption should be present.
+    EXPECT_FALSE(option->getOption(1));
 
     // Check that option will not be created if empty buffer is provided.
     buf.clear();
@@ -416,7 +535,7 @@ TEST_F(OptionCustomTest, stringData) {
 // The purpose of this test is to verify that the option definition comprising
 // an array of boolean values can be used to create an instance of custom option.
 TEST_F(OptionCustomTest, booleanDataArray) {
-    OptionDefinition opt_def("OPTION_FOO", 1000, "boolean", true);
+    OptionDefinition opt_def("option-foo", 1000, "boolean", true);
 
     // Create a buffer with 5 values that represent array of
     // booleans.
@@ -472,7 +591,7 @@ TEST_F(OptionCustomTest, booleanDataArray) {
 // an array of 32-bit signed integer values can be used to create an instance
 // of custom option.
 TEST_F(OptionCustomTest, uint32DataArray) {
-    OptionDefinition opt_def("OPTION_FOO", 1000, "uint32", true);
+    OptionDefinition opt_def("option-foo", 1000, "uint32", true);
 
     // Create an input buffer that holds 4 uint32 values that
     // represent an array.
@@ -656,6 +775,47 @@ TEST_F(OptionCustomTest, fqdnDataArray) {
     EXPECT_EQ("example.com.", domain1);
 }
 
+// The purpose of this test is to verify that the opton definition comprising
+// a record of fixed-size fields can be used to create an option with a
+// suboption.
+TEST_F(OptionCustomTest, recordDataWithSuboption) {
+    OptionDefinition opt_def("option-foo", 1000, "record", "option-foo-space");
+    ASSERT_NO_THROW(opt_def.addRecordField("uint32"));
+    ASSERT_NO_THROW(opt_def.addRecordField("ipv4-address"));
+
+    // Create a buffer with two fields: 4-byte number and IPv4 address.
+    OptionBuffer buf;
+    writeInt<uint32_t>(0x01020304, buf);
+    writeAddress(IOAddress("192.168.0.1"), buf);
+
+    // Append a suboption. It should be correctly parsed because option fields
+    // preceding this option have fixed (known) size.
+    appendV6Suboption(buf);
+
+    boost::scoped_ptr<OptionCustom> option;
+    ASSERT_NO_THROW(
+         option.reset(new OptionCustom(opt_def, Option::V6, buf.begin(),
+                                       buf.end()));
+    );
+    ASSERT_TRUE(option);
+
+    // We should have two data fields parsed.
+    ASSERT_EQ(2, option->getDataFieldsNum());
+
+    // Validate values in fields.
+    uint32_t value0 = 0;
+    ASSERT_NO_THROW(value0 = option->readInteger<uint32_t>(0));
+    EXPECT_EQ(0x01020304, value0);
+
+    IOAddress value1 = 0;
+    ASSERT_NO_THROW(value1 = option->readAddress(1));
+    EXPECT_EQ("192.168.0.1", value1.toText());
+
+    // Parsed option should have one suboption.
+    EXPECT_TRUE(hasV6Suboption(option.get()));
+
+}
+
 // The purpose of this test is to verify that the option definition comprising
 // a record of various data fields can be used to create an instance of
 // custom option.
diff --git a/src/lib/dhcp/tests/option_definition_unittest.cc b/src/lib/dhcp/tests/option_definition_unittest.cc
index 723aeef..357ed56 100644
--- a/src/lib/dhcp/tests/option_definition_unittest.cc
+++ b/src/lib/dhcp/tests/option_definition_unittest.cc
@@ -439,7 +439,7 @@ TEST_F(OptionDefinitionTest, ipv4AddressArrayTokenized) {
     EXPECT_TRUE(std::equal(addrs.begin(), addrs.end(), addrs_returned.begin()));
 }
 
-// The purpose of thie test is to verify that option definition for
+// The purpose of this test is to verify that option definition for
 // 'empty' option can be created and that it returns 'empty' option.
 TEST_F(OptionDefinitionTest, empty) {
     OptionDefinition opt_def("OPTION_RAPID_COMMIT", D6O_RAPID_COMMIT, "empty");
@@ -464,6 +464,49 @@ TEST_F(OptionDefinitionTest, empty) {
     EXPECT_EQ(0, option_v4->getData().size());
 }
 
+// The purpose of this test is to verify that when the empty option encapsulates
+// some option space, an instance of the OptionCustom is returned and its
+// suboptions are decoded.
+TEST_F(OptionDefinitionTest, emptyWithSuboptions) {
+    // Create an instance of the 'empty' option definition. This option
+    // encapsulates 'option-foo-space' so when we create a new option
+    // with this definition the OptionCustom should be returned. The
+    // Option Custom is generic option which support variety of formats
+    // and supports decoding suboptions.
+    OptionDefinition opt_def("option-foo", 1024, "empty", "option-foo-space");
+
+    // Define a suboption.
+    const uint8_t subopt_data[] = {
+        0x04, 0x01,  // Option code 1025
+        0x00, 0x04,  // Option len = 4
+        0x01, 0x02, 0x03, 0x04 // Option data
+    };
+
+    // Create an option, having option code 1024 from the definition. Pass
+    // the option buffer containing suboption.
+    OptionPtr option_v6;
+    ASSERT_NO_THROW(
+        option_v6 = opt_def.optionFactory(Option::V6, 1024,
+                                          OptionBuffer(subopt_data,
+                                                       subopt_data +
+                                                       sizeof(subopt_data)))
+    );
+    // Returned option should be of the OptionCustom type.
+    ASSERT_TRUE(typeid(*option_v6) == typeid(OptionCustom));
+    // Sanity-check length, universe etc.
+    EXPECT_EQ(Option::V6, option_v6->getUniverse());
+    EXPECT_EQ(4, option_v6->getHeaderLen());
+    // This option should have one suboption with the code of 1025.
+    OptionPtr subopt_v6 = option_v6->getOption(1025);
+    EXPECT_TRUE(subopt_v6);
+    // Check that this suboption holds valid data.
+    EXPECT_EQ(1025, subopt_v6->getType());
+    EXPECT_EQ(Option::V6, subopt_v6->getUniverse());
+    EXPECT_EQ(0, memcmp(&subopt_v6->getData()[0], subopt_data + 4, 4));
+
+    // @todo consider having a similar test for V4.
+}
+
 // The purpose of this test is to verify that definition can be
 // creates for the option that holds binary data.
 TEST_F(OptionDefinitionTest, binary) {
diff --git a/src/lib/dhcp/tests/option_unittest.cc b/src/lib/dhcp/tests/option_unittest.cc
index 3a47d0e..a3aea9f 100644
--- a/src/lib/dhcp/tests/option_unittest.cc
+++ b/src/lib/dhcp/tests/option_unittest.cc
@@ -61,6 +61,8 @@ public:
     /// Contains custom implementation of the callback.
     ///
     /// @param buf a A buffer holding options in on-wire format.
+    /// @param option_space A name of the option space being encapsulated by
+    /// the option being parsed.
     /// @param [out] options A reference to the collection where parsed options
     /// will be stored.
     /// @param relay_msg_offset Reference to a size_t structure. If specified,
@@ -69,7 +71,7 @@ public:
     /// length of the relay_msg option will be stored in it.
     /// @return An offset to the first byte after last parsed option.
     size_t execute(const OptionBuffer& buf,
-                   const std::string&,
+                   const std::string& option_space,
                    isc::dhcp::OptionCollection& options,
                    size_t* relay_msg_offset,
                    size_t* relay_msg_len) {
@@ -77,7 +79,7 @@ public:
         // callback has been actually called.
         executed_ = true;
         // Use default implementation of the unpack algorithm to parse options.
-        return (LibDHCP::unpackOptions6(buf, options, relay_msg_offset,
+        return (LibDHCP::unpackOptions6(buf, option_space, options, relay_msg_offset,
                                         relay_msg_len));
     }
 
diff --git a/src/lib/dhcp/tests/pkt4_unittest.cc b/src/lib/dhcp/tests/pkt4_unittest.cc
index 242e7d7..72ffff7 100644
--- a/src/lib/dhcp/tests/pkt4_unittest.cc
+++ b/src/lib/dhcp/tests/pkt4_unittest.cc
@@ -66,17 +66,19 @@ public:
     /// Contains custom implementation of the callback.
     ///
     /// @param buf a A buffer holding options in on-wire format.
+    /// @param option_space A name of the option space being encapsulated by
+    /// the option being parsed.
     /// @param [out] options A reference to the collection where parsed options
     /// will be stored.
     /// @return An offset to the first byte after last parsed option.
     size_t execute(const OptionBuffer& buf,
-                   const std::string&,
+                   const std::string& option_space,
                    isc::dhcp::OptionCollection& options) {
         // Set the executed_ member to true to allow verification that the
         // callback has been actually called.
         executed_ = true;
         // Use default implementation of the unpack algorithm to parse options.
-        return (LibDHCP::unpackOptions4(buf, options));
+        return (LibDHCP::unpackOptions4(buf, option_space, options));
     }
 
     /// A flag which indicates if callback function has been called.
diff --git a/src/lib/dhcp/tests/pkt6_unittest.cc b/src/lib/dhcp/tests/pkt6_unittest.cc
index 0687321..e18b545 100644
--- a/src/lib/dhcp/tests/pkt6_unittest.cc
+++ b/src/lib/dhcp/tests/pkt6_unittest.cc
@@ -66,6 +66,8 @@ public:
     /// Contains custom implementation of the callback.
     ///
     /// @param buf a A buffer holding options in on-wire format.
+    /// @param option_space A name of the option space encapsulated by the
+    /// option being parsed.
     /// @param [out] options A reference to the collection where parsed options
     /// will be stored.
     /// @param relay_msg_offset Reference to a size_t structure. If specified,
@@ -74,7 +76,7 @@ public:
     /// length of the relay_msg option will be stored in it.
     /// @return An offset to the first byte after last parsed option.
     size_t execute(const OptionBuffer& buf,
-                   const std::string&,
+                   const std::string& option_space,
                    isc::dhcp::OptionCollection& options,
                    size_t* relay_msg_offset,
                    size_t* relay_msg_len) {
@@ -82,7 +84,7 @@ public:
         // callback has been actually called.
         executed_ = true;
         // Use default implementation of the unpack algorithm to parse options.
-        return (LibDHCP::unpackOptions6(buf, options, relay_msg_offset,
+        return (LibDHCP::unpackOptions6(buf, option_space, options, relay_msg_offset,
                                         relay_msg_len));
     }
 



More information about the bind10-changes mailing list