BIND 10 master, updated. 27e6119093723a1e46a239ec245a8b4b10677635 [master] Merge branch 'trac2314'

BIND 10 source code commits bind10-changes at lists.isc.org
Tue Jan 22 18:13:10 UTC 2013


The branch, master has been updated
       via  27e6119093723a1e46a239ec245a8b4b10677635 (commit)
       via  79acb1197b7046a21034d5a1dadeb6b2efbf030a (commit)
       via  0be5967e2ff6364d8d38ad297be99925dfe20cc4 (commit)
       via  3886d1174c6552cc99c3d5fa2c727c3ee0bcc281 (commit)
       via  3f1577b50df4375e51d94be2650ab2ae5e4d6698 (commit)
       via  30276d77d28c951226859d2f1f452abfabdcf6f4 (commit)
       via  2cecfb45e98d30493019d4664fc7ca320e377a2e (commit)
       via  f56d5006adee1ae27e38960d26e46b5fe488c965 (commit)
       via  fc075a50ee9a1d31c4851afc1f6df64c67951547 (commit)
       via  6a2437122d5dddfc0ddfd923c5036331ca23fb12 (commit)
       via  f16d079d34dc61a5cea082328c3eb38beeea3d7c (commit)
       via  45b03507d6ec3868648713f86e7789b15f78a367 (commit)
       via  656b5d1cb045b35499ced064fae6bc1962f91ad6 (commit)
       via  77c9f2b09e47184c42f56658dcd93c4eee36587a (commit)
      from  de49cb6406849be06c5452ca532bb1007fe5c509 (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 27e6119093723a1e46a239ec245a8b4b10677635
Merge: de49cb6 79acb11
Author: Marcin Siodelski <marcin at isc.org>
Date:   Tue Jan 22 19:12:58 2013 +0100

    [master] Merge branch 'trac2314'
    
    Conflicts:
    	src/bin/dhcp6/tests/config_parser_unittest.cc

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

Summary of changes:
 src/bin/dhcp4/config_parser.cc                     |  116 ++++-
 src/bin/dhcp4/dhcp4.spec                           |    6 +
 src/bin/dhcp4/tests/config_parser_unittest.cc      |  490 +++++++++++++++++-
 src/bin/dhcp6/config_parser.cc                     |  116 ++++-
 src/bin/dhcp6/dhcp6.spec                           |    6 +
 src/bin/dhcp6/dhcp6_srv.cc                         |    9 +-
 src/bin/dhcp6/tests/config_parser_unittest.cc      |  530 +++++++++++++++++---
 src/lib/dhcp/Makefile.am                           |    1 +
 src/lib/dhcp/libdhcp++.cc                          |   65 ++-
 src/lib/dhcp/option_custom.h                       |    5 +-
 src/lib/dhcp/option_definition.cc                  |   39 +-
 src/lib/dhcp/option_definition.h                   |   66 ++-
 src/lib/{dhcpsrv => dhcp}/option_space.cc          |    2 +-
 src/lib/{dhcpsrv => dhcp}/option_space.h           |    0
 src/lib/dhcp/std_option_defs.h                     |  244 ++++-----
 src/lib/dhcp/tests/Makefile.am                     |    1 +
 src/lib/dhcp/tests/libdhcp++_unittest.cc           |   34 +-
 src/lib/dhcp/tests/option_definition_unittest.cc   |   51 +-
 .../tests/option_space_unittest.cc                 |    2 +-
 src/lib/dhcpsrv/Makefile.am                        |    1 -
 src/lib/dhcpsrv/cfgmgr.h                           |    2 +-
 src/lib/dhcpsrv/tests/Makefile.am                  |    1 -
 22 files changed, 1497 insertions(+), 290 deletions(-)
 rename src/lib/{dhcpsrv => dhcp}/option_space.cc (98%)
 rename src/lib/{dhcpsrv => dhcp}/option_space.h (100%)
 rename src/lib/{dhcpsrv => dhcp}/tests/option_space_unittest.cc (99%)

-----------------------------------------------------------------------
diff --git a/src/bin/dhcp4/config_parser.cc b/src/bin/dhcp4/config_parser.cc
index 67a7ec6..ddb2dc4 100644
--- a/src/bin/dhcp4/config_parser.cc
+++ b/src/bin/dhcp4/config_parser.cc
@@ -766,7 +766,10 @@ private:
         }
 
         std::string option_space = getParam<std::string>("space", string_values_);
-        /// @todo Validate option space once #2313 is merged.
+        if (!OptionSpace::validateName(option_space)) {
+            isc_throw(DhcpConfigError, "invalid option space name'"
+                      << option_space << "'");
+        }
 
         OptionDefinitionPtr def;
         if (option_space == "dhcp4" &&
@@ -855,7 +858,7 @@ private:
             // definition of option value makes sense.
             if (def->getName() != option_name) {
                 isc_throw(DhcpConfigError, "specified option name '"
-                          << option_name << " does not match the "
+                          << option_name << "' does not match the "
                           << "option definition: '" << option_space
                           << "." << def->getName() << "'");
             }
@@ -875,6 +878,7 @@ private:
                           << ", code: " << option_code << "): "
                           << ex.what());
             }
+
         }
         // All went good, so we can set the option space name.
         option_space_ = option_space;
@@ -976,7 +980,7 @@ public:
 /// @brief Parser for a single option definition.
 ///
 /// This parser creates an instance of a single option definition.
-class OptionDefParser: DhcpConfigParser {
+class OptionDefParser : public DhcpConfigParser {
 public:
 
     /// @brief Constructor.
@@ -1003,7 +1007,8 @@ public:
             std::string entry(param.first);
             ParserPtr parser;
             if (entry == "name" || entry == "type" ||
-                entry == "record-types" || entry == "space") {
+                entry == "record-types" || entry == "space" ||
+                entry == "encapsulate") {
                 StringParserPtr
                     str_parser(dynamic_cast<StringParser*>(StringParser::factory(entry)));
                 if (str_parser) {
@@ -1053,8 +1058,8 @@ public:
 
     /// @brief Stores the parsed option definition in a storage.
     void commit() {
-        // @todo validate option space name once 2313 is merged.
-        if (storage_ && option_definition_) {
+        if (storage_ && option_definition_ &&
+            OptionSpace::validateName(option_space_name_)) {
             storage_->addItem(option_definition_, option_space_name_);
         }
     }
@@ -1076,11 +1081,10 @@ private:
     void createOptionDef() {
         // Get the option space name and validate it.
         std::string space = getParam<std::string>("space", string_values_);
-        // @todo uncomment the code below when the #2313 is merged.
-        /*        if (!OptionSpace::validateName()) {
+        if (!OptionSpace::validateName(space)) {
             isc_throw(DhcpConfigError, "invalid option space name '"
                       << space << "'");
-                      } */
+        }
 
         // Get other parameters that are needed to create the
         // option definition.
@@ -1088,9 +1092,35 @@ private:
         uint32_t code = getParam<uint32_t>("code", uint32_values_);
         std::string type = getParam<std::string>("type", string_values_);
         bool array_type = getParam<bool>("array", boolean_values_);
+        std::string encapsulates = getParam<std::string>("encapsulate",
+                                                         string_values_);
+
+        // Create option definition.
+        OptionDefinitionPtr def;
+        // We need to check if user has set encapsulated option space
+        // name. If so, different constructor will be used.
+        if (!encapsulates.empty()) {
+            // Arrays can't be used together with sub-options.
+            if (array_type) {
+                isc_throw(DhcpConfigError, "option '" << space << "."
+                          << "name" << "', comprising an array of data"
+                          << " fields may not encapsulate any option space");
+
+            } else if (encapsulates == space) {
+                isc_throw(DhcpConfigError, "option must not encapsulate"
+                          << " an option space it belongs to: '"
+                          << space << "." << name << "' is set to"
+                          << " encapsulate '" << space << "'");
+
+            } else {
+                def.reset(new OptionDefinition(name, code, type,
+                                               encapsulates.c_str()));
+            }
+
+        } else {
+            def.reset(new OptionDefinition(name, code, type, array_type));
 
-        OptionDefinitionPtr def(new OptionDefinition(name, code,
-                                                     type, array_type));
+        }
         // The record-types field may carry a list of comma separated names
         // of data types that form a record.
         std::string record_types = getParam<std::string>("record-types",
@@ -1108,7 +1138,7 @@ private:
                 }
             } catch (const Exception& ex) {
                 isc_throw(DhcpConfigError, "invalid record type values"
-                          << " specified for the option  definition: "
+                          << " specified for the option definition: "
                           << ex.what());
             }
         }
@@ -1330,6 +1360,63 @@ private:
         return (false);
     }
 
+    /// @brief Append sub-options to an option.
+    ///
+    /// @param option_space a name of the encapsulated option space.
+    /// @param option option instance to append sub-options to.
+    void appendSubOptions(const std::string& option_space, OptionPtr& option) {
+        // Only non-NULL options are stored in option container.
+        // If this option pointer is NULL this is a serious error.
+        assert(option);
+
+        OptionDefinitionPtr def;
+        if (option_space == "dhcp4" &&
+            LibDHCP::isStandardOption(Option::V4, option->getType())) {
+            def = LibDHCP::getOptionDef(Option::V4, option->getType());
+            // Definitions for some of the standard options hasn't been
+            // implemented so it is ok to leave here.
+            if (!def) {
+                return;
+            }
+        } else {
+            const OptionDefContainerPtr defs =
+                option_def_intermediate.getItems(option_space);
+            const OptionDefContainerTypeIndex& idx = defs->get<1>();
+            const OptionDefContainerTypeRange& range =
+                idx.equal_range(option->getType());
+            // There is no definition so we have to leave.
+            if (std::distance(range.first, range.second) == 0) {
+                return;
+            }
+
+            def = *range.first;
+
+            // If the definition exists, it must be non-NULL.
+            // Otherwise it is a programming error.
+            assert(def);
+        }
+
+        // We need to get option definition for the particular option space
+        // and code. This definition holds the information whether our
+        // option encapsulates any option space.
+        // Get the encapsulated option space name.
+        std::string encapsulated_space = def->getEncapsulatedSpace();
+        // If option space name is empty it means that our option does not
+        // encapsulate any option space (does not include sub-options).
+        if (!encapsulated_space.empty()) {
+            // Get the sub-options that belong to the encapsulated
+            // option space.
+            const Subnet::OptionContainerPtr sub_opts =
+                option_defaults.getItems(encapsulated_space);
+            // Append sub-options to the option.
+            BOOST_FOREACH(Subnet::OptionDescriptor desc, *sub_opts) {
+                if (desc.option) {
+                    option->addOption(desc.option);
+                }
+            }
+        }
+    }
+
     /// @brief Create a new subnet using a data from child parsers.
     ///
     /// @throw isc::dhcp::DhcpConfigError if subnet configuration parsing failed.
@@ -1405,6 +1492,8 @@ private:
                     LOG_WARN(dhcp4_logger, DHCP4_CONFIG_OPTION_DUPLICATE)
                         .arg(desc.option->getType()).arg(addr.toText());
                 }
+                // Add sub-options (if any).
+                appendSubOptions(option_space, desc.option);
                 // In any case, we add the option to the subnet.
                 subnet_->addOption(desc.option, false, option_space);
             }
@@ -1432,6 +1521,9 @@ private:
                 Subnet::OptionDescriptor existing_desc =
                     subnet_->getOptionDescriptor(option_space, desc.option->getType());
                 if (!existing_desc.option) {
+                    // Add sub-options (if any).
+                    appendSubOptions(option_space, desc.option);
+
                     subnet_->addOption(desc.option, false, option_space);
                 }
             }
diff --git a/src/bin/dhcp4/dhcp4.spec b/src/bin/dhcp4/dhcp4.spec
index b139d82..ea58c7f 100644
--- a/src/bin/dhcp4/dhcp4.spec
+++ b/src/bin/dhcp4/dhcp4.spec
@@ -80,6 +80,12 @@
             "item_type": "string",
             "item_optional": false,
             "item_default": ""
+          },
+
+          { "item_name": "encapsulate",
+            "item_type": "string",
+            "item_optional": false,
+            "item_default": ""
           } ]
         }
       },
diff --git a/src/bin/dhcp4/tests/config_parser_unittest.cc b/src/bin/dhcp4/tests/config_parser_unittest.cc
index ba14edf..7fbf854 100644
--- a/src/bin/dhcp4/tests/config_parser_unittest.cc
+++ b/src/bin/dhcp4/tests/config_parser_unittest.cc
@@ -21,6 +21,8 @@
 #include <dhcp4/dhcp4_srv.h>
 #include <dhcp4/config_parser.h>
 #include <dhcp/option4_addrlst.h>
+#include <dhcp/option_custom.h>
+#include <dhcp/option_int.h>
 #include <dhcpsrv/subnet.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <boost/foreach.hpp>
@@ -61,7 +63,7 @@ public:
         EXPECT_EQ(expected_value, it->second);
     }
 
-    // Checks if config_result (result of DHCP server configuration) has
+    // Checks if the result of DHCP server configuration has
     // expected code (0 for success, other for failures).
     // Also stores result in rcode_ and comment_.
     void checkResult(ConstElementPtr status, int expected_code) {
@@ -461,7 +463,8 @@ TEST_F(Dhcp4ParserTest, optionDefIpv4Address) {
         "      \"type\": \"ipv4-address\","
         "      \"array\": False,"
         "      \"record-types\": \"\","
-        "      \"space\": \"isc\""
+        "      \"space\": \"isc\","
+        "      \"encapsulate\": \"\""
         "  } ]"
         "}";
     ElementPtr json = Element::fromJSON(config);
@@ -474,6 +477,7 @@ TEST_F(Dhcp4ParserTest, optionDefIpv4Address) {
     ConstElementPtr status;
     EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
     ASSERT_TRUE(status);
+    checkResult(status, 0);
 
     // The option definition should now be available in the CfgMgr.
     def = CfgMgr::instance().getOptionDef("isc", 100);
@@ -484,6 +488,7 @@ TEST_F(Dhcp4ParserTest, optionDefIpv4Address) {
     EXPECT_EQ(100, def->getCode());
     EXPECT_FALSE(def->getArrayType());
     EXPECT_EQ(OPT_IPV4_ADDRESS_TYPE, def->getType());
+    EXPECT_TRUE(def->getEncapsulatedSpace().empty());
 }
 
 // The goal of this test is to check whether an option definiiton
@@ -499,7 +504,8 @@ TEST_F(Dhcp4ParserTest, optionDefRecord) {
         "      \"type\": \"record\","
         "      \"array\": False,"
         "      \"record-types\": \"uint16, ipv4-address, ipv6-address, string\","
-        "      \"space\": \"isc\""
+        "      \"space\": \"isc\","
+        "      \"encapsulate\": \"\""
         "  } ]"
         "}";
     ElementPtr json = Element::fromJSON(config);
@@ -523,6 +529,7 @@ TEST_F(Dhcp4ParserTest, optionDefRecord) {
     EXPECT_EQ(100, def->getCode());
     EXPECT_EQ(OPT_RECORD_TYPE, def->getType());
     EXPECT_FALSE(def->getArrayType());
+    EXPECT_TRUE(def->getEncapsulatedSpace().empty());
 
     // The option comprises the record of data fields. Verify that all
     // fields are present and they are of the expected types.
@@ -546,7 +553,8 @@ TEST_F(Dhcp4ParserTest, optionDefMultiple) {
         "      \"type\": \"uint32\","
         "      \"array\": False,"
         "      \"record-types\": \"\","
-        "      \"space\": \"isc\""
+        "      \"space\": \"isc\","
+        "      \"encapsulate\": \"\""
         "  },"
         "  {"
         "      \"name\": \"foo-2\","
@@ -554,7 +562,8 @@ TEST_F(Dhcp4ParserTest, optionDefMultiple) {
         "      \"type\": \"ipv4-address\","
         "      \"array\": False,"
         "      \"record-types\": \"\","
-        "      \"space\": \"isc\""
+        "      \"space\": \"isc\","
+        "      \"encapsulate\": \"\""
         "  } ]"
         "}";
     ElementPtr json = Element::fromJSON(config);
@@ -578,6 +587,7 @@ TEST_F(Dhcp4ParserTest, optionDefMultiple) {
     EXPECT_EQ(100, def1->getCode());
     EXPECT_EQ(OPT_UINT32_TYPE, def1->getType());
     EXPECT_FALSE(def1->getArrayType());
+    EXPECT_TRUE(def1->getEncapsulatedSpace().empty());
 
     // Check the second option definition we have created.
     OptionDefinitionPtr def2 = CfgMgr::instance().getOptionDef("isc", 101);
@@ -588,6 +598,7 @@ TEST_F(Dhcp4ParserTest, optionDefMultiple) {
     EXPECT_EQ(101, def2->getCode());
     EXPECT_EQ(OPT_IPV4_ADDRESS_TYPE, def2->getType());
     EXPECT_FALSE(def2->getArrayType());
+    EXPECT_TRUE(def2->getEncapsulatedSpace().empty());
 }
 
 // The goal of this test is to verify that the duplicated option
@@ -604,7 +615,8 @@ TEST_F(Dhcp4ParserTest, optionDefDuplicate) {
         "      \"type\": \"uint32\","
         "      \"array\": False,"
         "      \"record-types\": \"\","
-        "      \"space\": \"isc\""
+        "      \"space\": \"isc\","
+        "      \"encapsulate\": \"\""
         "  },"
         "  {"
         "      \"name\": \"foo-2\","
@@ -612,7 +624,8 @@ TEST_F(Dhcp4ParserTest, optionDefDuplicate) {
         "      \"type\": \"ipv4-address\","
         "      \"array\": False,"
         "      \"record-types\": \"\","
-        "      \"space\": \"isc\""
+        "      \"space\": \"isc\","
+        "      \"encapsulate\": \"\""
         "  } ]"
         "}";
     ElementPtr json = Element::fromJSON(config);
@@ -640,7 +653,8 @@ TEST_F(Dhcp4ParserTest, optionDefArray) {
         "      \"type\": \"uint32\","
         "      \"array\": True,"
         "      \"record-types\": \"\","
-        "      \"space\": \"isc\""
+        "      \"space\": \"isc\","
+        "      \"encapsulate\": \"\""
         "  } ]"
         "}";
     ElementPtr json = Element::fromJSON(config);
@@ -664,6 +678,48 @@ TEST_F(Dhcp4ParserTest, optionDefArray) {
     EXPECT_EQ(100, def->getCode());
     EXPECT_EQ(OPT_UINT32_TYPE, def->getType());
     EXPECT_TRUE(def->getArrayType());
+    EXPECT_TRUE(def->getEncapsulatedSpace().empty());
+}
+
+// The purpose of this test to verify that encapsulated option
+// space name may be specified.
+TEST_F(Dhcp4ParserTest, optionDefEncapsulate) {
+
+    // Configuration string. Included the encapsulated
+    // option space name.
+    std::string config =
+        "{ \"option-def\": [ {"
+        "      \"name\": \"foo\","
+        "      \"code\": 100,"
+        "      \"type\": \"uint32\","
+        "      \"array\": False,"
+        "      \"record-types\": \"\","
+        "      \"space\": \"isc\","
+        "      \"encapsulate\": \"sub-opts-space\""
+        "  } ]"
+        "}";
+    ElementPtr json = Element::fromJSON(config);
+
+    // Make sure that the particular option definition does not exist.
+    OptionDefinitionPtr def = CfgMgr::instance().getOptionDef("isc", 100);
+    ASSERT_FALSE(def);
+
+    // Use the configuration string to create new option definition.
+    ConstElementPtr status;
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
+    ASSERT_TRUE(status);
+    checkResult(status, 0);
+
+    // The option definition should now be available in the CfgMgr.
+    def = CfgMgr::instance().getOptionDef("isc", 100);
+    ASSERT_TRUE(def);
+
+    // Check the option data.
+    EXPECT_EQ("foo", def->getName());
+    EXPECT_EQ(100, def->getCode());
+    EXPECT_EQ(OPT_UINT32_TYPE, def->getType());
+    EXPECT_FALSE(def->getArrayType());
+    EXPECT_EQ("sub-opts-space", def->getEncapsulatedSpace());
 }
 
 /// The purpose of this test is to verify that the option definition
@@ -678,7 +734,8 @@ TEST_F(Dhcp4ParserTest, optionDefInvalidName) {
         "      \"type\": \"string\","
         "      \"array\": False,"
         "      \"record-types\": \"\","
-        "      \"space\": \"isc\""
+        "      \"space\": \"isc\","
+        "      \"encapsulate\": \"\""
         "  } ]"
         "}";
     ElementPtr json = Element::fromJSON(config);
@@ -703,7 +760,8 @@ TEST_F(Dhcp4ParserTest, optionDefInvalidType) {
         "      \"type\": \"sting\","
         "      \"array\": False,"
         "      \"record-types\": \"\","
-        "      \"space\": \"isc\""
+        "      \"space\": \"isc\","
+        "      \"encapsulate\": \"\""
         "  } ]"
         "}";
     ElementPtr json = Element::fromJSON(config);
@@ -728,7 +786,62 @@ TEST_F(Dhcp4ParserTest, optionDefInvalidRecordType) {
         "      \"type\": \"record\","
         "      \"array\": False,"
         "      \"record-types\": \"uint32,uint8,sting\","
-        "      \"space\": \"isc\""
+        "      \"space\": \"isc\","
+        "      \"encapsulate\": \"\""
+        "  } ]"
+        "}";
+    ElementPtr json = Element::fromJSON(config);
+
+    // Use the configuration string to create new option definition.
+    ConstElementPtr status;
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
+    ASSERT_TRUE(status);
+    // Expecting parsing error (error code 1).
+    checkResult(status, 1);
+}
+
+/// The goal of this test is to verify that the invalid encapsulated
+/// option space name is not accepted.
+TEST_F(Dhcp4ParserTest, optionDefInvalidEncapsulatedSpace) {
+    // Configuration string. The encapsulated option space
+    // name is invalid (% character is not allowed).
+    std::string config =
+        "{ \"option-def\": [ {"
+        "      \"name\": \"foo\","
+        "      \"code\": 100,"
+        "      \"type\": \"uint32\","
+        "      \"array\": False,"
+        "      \"record-types\": \"\","
+        "      \"space\": \"isc\","
+        "      \"encapsulate\": \"invalid%space%name\""
+        "  } ]"
+        "}";
+    ElementPtr json = Element::fromJSON(config);
+
+    // Use the configuration string to create new option definition.
+    ConstElementPtr status;
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
+    ASSERT_TRUE(status);
+    // Expecting parsing error (error code 1).
+    checkResult(status, 1);
+}
+
+/// The goal of this test is to verify that the encapsulated
+/// option space name can't be specified for the option that
+/// comprises an array of data fields.
+TEST_F(Dhcp4ParserTest, optionDefEncapsulatedSpaceAndArray) {
+    // Configuration string. The encapsulated option space
+    // name is set to non-empty value and the array flag
+    // is set.
+    std::string config =
+        "{ \"option-def\": [ {"
+        "      \"name\": \"foo\","
+        "      \"code\": 100,"
+        "      \"type\": \"uint32\","
+        "      \"array\": True,"
+        "      \"record-types\": \"\","
+        "      \"space\": \"isc\","
+        "      \"encapsulate\": \"valid-space-name\""
         "  } ]"
         "}";
     ElementPtr json = Element::fromJSON(config);
@@ -741,6 +854,31 @@ TEST_F(Dhcp4ParserTest, optionDefInvalidRecordType) {
     checkResult(status, 1);
 }
 
+/// The goal of this test is to verify that the option may not
+/// encapsulate option space it belongs to.
+TEST_F(Dhcp4ParserTest, optionDefEncapsulateOwnSpace) {
+    // Configuration string. Option is set to encapsulate
+    // option space it belongs to.
+    std::string config =
+        "{ \"option-def\": [ {"
+        "      \"name\": \"foo\","
+        "      \"code\": 100,"
+        "      \"type\": \"uint32\","
+        "      \"array\": False,"
+        "      \"record-types\": \"\","
+        "      \"space\": \"isc\","
+        "      \"encapsulate\": \"isc\""
+        "  } ]"
+        "}";
+    ElementPtr json = Element::fromJSON(config);
+
+    // Use the configuration string to create new option definition.
+    ConstElementPtr status;
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
+    ASSERT_TRUE(status);
+    // Expecting parsing error (error code 1).
+    checkResult(status, 1);
+}
 
 /// The purpose of this test is to verify that it is not allowed
 /// to override the standard option (that belongs to dhcp4 option
@@ -759,7 +897,8 @@ TEST_F(Dhcp4ParserTest, optionStandardDefOverride) {
         "      \"type\": \"string\","
         "      \"array\": False,"
         "      \"record-types\": \"\","
-        "      \"space\": \"dhcp4\""
+        "      \"space\": \"dhcp4\","
+        "      \"encapsulate\": \"\""
         "  } ]"
         "}";
     ElementPtr json = Element::fromJSON(config);
@@ -794,7 +933,8 @@ TEST_F(Dhcp4ParserTest, optionStandardDefOverride) {
         "      \"type\": \"string\","
         "      \"array\": False,"
         "      \"record-types\": \"\","
-        "      \"space\": \"dhcp4\""
+        "      \"space\": \"dhcp4\","
+        "      \"encapsulate\": \"\""
         "  } ]"
         "}";
     json = Element::fromJSON(config);
@@ -907,7 +1047,8 @@ TEST_F(Dhcp4ParserTest, optionDataTwoSpaces) {
         "    \"type\": \"uint32\","
         "    \"array\": False,"
         "    \"record-types\": \"\","
-        "    \"space\": \"isc\""
+        "    \"space\": \"isc\","
+        "    \"encapsulate\": \"\""
         " } ],"
         "\"subnet4\": [ { "
         "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
@@ -940,6 +1081,166 @@ TEST_F(Dhcp4ParserTest, optionDataTwoSpaces) {
     ASSERT_FALSE(desc3.option);
 }
 
+// The goal of this test is to verify that it is possible to
+// encapsulate option space containing some options with
+// another option. In this test we create base option that
+// encapsulates option space 'isc' that comprises two other
+// options. Also, for all options their definitions are
+// created.
+TEST_F(Dhcp4ParserTest, optionDataEncapsulate) {
+
+    // @todo DHCP configurations has many dependencies between
+    // parameters. First of all, configuration for subnet is
+    // inherited from the global values. Thus subnet has to be
+    // configured when all global values have been configured.
+    // Also, an option can encapsulate another option only
+    // if the latter has been configured. For this reason in this
+    // test we created two-stage configuration where first we
+    // created options that belong to encapsulated option space.
+    // In the second stage we add the base option. Also, the Subnet
+    // object is configured in the second stage so it is created
+    // at the very end (when all other parameters are configured).
+
+    // Starting stage 1. Configure sub-options and their definitions.
+    string config = "{ \"interface\": [ \"all\" ],"
+        "\"rebind-timer\": 2000,"
+        "\"renew-timer\": 1000,"
+        "\"option-data\": [ {"
+        "    \"name\": \"foo\","
+        "    \"space\": \"isc\","
+        "    \"code\": 1,"
+        "    \"data\": \"1234\","
+        "    \"csv-format\": True"
+        " },"
+        " {"
+        "    \"name\": \"foo2\","
+        "    \"space\": \"isc\","
+        "    \"code\": 2,"
+        "    \"data\": \"192.168.2.1\","
+        "    \"csv-format\": True"
+        " } ],"
+        "\"option-def\": [ {"
+        "    \"name\": \"foo\","
+        "    \"code\": 1,"
+        "    \"type\": \"uint32\","
+        "    \"array\": False,"
+        "    \"record-types\": \"\","
+        "    \"space\": \"isc\","
+        "    \"encapsulate\": \"\""
+        " },"
+        " {"
+        "    \"name\": \"foo2\","
+        "    \"code\": 2,"
+        "    \"type\": \"ipv4-address\","
+        "    \"array\": False,"
+        "    \"record-types\": \"\","
+        "    \"space\": \"isc\","
+        "    \"encapsulate\": \"\""
+        " } ]"
+        "}";
+
+    ConstElementPtr status;
+
+    ElementPtr json = Element::fromJSON(config);
+
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
+    ASSERT_TRUE(status);
+    checkResult(status, 0);
+
+    // Stage 2. Configure base option and a subnet. Please note that
+    // the configuration from the stage 2 is repeated because BIND
+    // configuration manager sends whole configuration for the lists
+    // where at least one element is being modified or added.
+    config = "{ \"interface\": [ \"all\" ],"
+        "\"rebind-timer\": 2000,"
+        "\"renew-timer\": 1000,"
+        "\"option-data\": [ {"
+        "    \"name\": \"base-option\","
+        "    \"space\": \"dhcp4\","
+        "    \"code\": 222,"
+        "    \"data\": \"11\","
+        "    \"csv-format\": True"
+        " },"
+        " {"
+        "    \"name\": \"foo\","
+        "    \"space\": \"isc\","
+        "    \"code\": 1,"
+        "    \"data\": \"1234\","
+        "    \"csv-format\": True"
+        " },"
+        " {"
+        "    \"name\": \"foo2\","
+        "    \"space\": \"isc\","
+        "    \"code\": 2,"
+        "    \"data\": \"192.168.2.1\","
+        "    \"csv-format\": True"
+        " } ],"
+        "\"option-def\": [ {"
+        "    \"name\": \"base-option\","
+        "    \"code\": 222,"
+        "    \"type\": \"uint8\","
+        "    \"array\": False,"
+        "    \"record-types\": \"\","
+        "    \"space\": \"dhcp4\","
+        "    \"encapsulate\": \"isc\""
+        "},"
+        "{"
+        "    \"name\": \"foo\","
+        "    \"code\": 1,"
+        "    \"type\": \"uint32\","
+        "    \"array\": False,"
+        "    \"record-types\": \"\","
+        "    \"space\": \"isc\","
+        "    \"encapsulate\": \"\""
+        " },"
+        " {"
+        "    \"name\": \"foo2\","
+        "    \"code\": 2,"
+        "    \"type\": \"ipv4-address\","
+        "    \"array\": False,"
+        "    \"record-types\": \"\","
+        "    \"space\": \"isc\","
+        "    \"encapsulate\": \"\""
+        " } ],"
+        "\"subnet4\": [ { "
+        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"subnet\": \"192.0.2.0/24\""
+        " } ]"
+        "}";
+
+
+    json = Element::fromJSON(config);
+
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
+    ASSERT_TRUE(status);
+    checkResult(status, 0);
+
+    // Get the subnet.
+    Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.5"));
+    ASSERT_TRUE(subnet);
+
+    // We should have one option available.
+    Subnet::OptionContainerPtr options = subnet->getOptionDescriptors("dhcp4");
+    ASSERT_TRUE(options);
+    ASSERT_EQ(1, options->size());
+
+    // Get the option.
+    Subnet::OptionDescriptor desc = subnet->getOptionDescriptor("dhcp4", 222);
+    EXPECT_TRUE(desc.option);
+    EXPECT_EQ(222, desc.option->getType());
+
+    // This opton should comprise two sub-options.
+    // One of them is 'foo' with code 1.
+    OptionPtr option_foo = desc.option->getOption(1);
+    ASSERT_TRUE(option_foo);
+    EXPECT_EQ(1, option_foo->getType());
+
+    // ...another one 'foo2' with code 2.
+    OptionPtr option_foo2 = desc.option->getOption(2);
+    ASSERT_TRUE(option_foo2);
+    EXPECT_EQ(2, option_foo2->getType());
+}
+
 // Goal of this test is to verify options configuration
 // for a single subnet. In particular this test checks
 // that local options configuration overrides global
@@ -1290,4 +1591,165 @@ TEST_F(Dhcp4ParserTest, DISABLED_Uint32Parser) {
     checkResult(status, 1);
 }
 
+// The goal of this test is to verify that the standard option can
+// be configured to encapsulate multiple other options.
+TEST_F(Dhcp4ParserTest, stdOptionDataEncapsulate) {
+
+    // The configuration is two stage process in this test.
+    // In the first stahe we create definitions of suboptions
+    // that we will add to the base option.
+    // Let's create some dummy options: foo and foo2.
+    string config = "{ \"interface\": [ \"all\" ],"
+        "\"rebind-timer\": 2000,"
+        "\"renew-timer\": 1000,"
+        "\"option-data\": [ {"
+        "    \"name\": \"foo\","
+        "    \"space\": \"vendor-encapsulated-options-space\","
+        "    \"code\": 1,"
+        "    \"data\": \"1234\","
+        "    \"csv-format\": True"
+        " },"
+        " {"
+        "    \"name\": \"foo2\","
+        "    \"space\": \"vendor-encapsulated-options-space\","
+        "    \"code\": 2,"
+        "    \"data\": \"192.168.2.1\","
+        "    \"csv-format\": True"
+        " } ],"
+        "\"option-def\": [ {"
+        "    \"name\": \"foo\","
+        "    \"code\": 1,"
+        "    \"type\": \"uint32\","
+        "    \"array\": False,"
+        "    \"record-types\": \"\","
+        "    \"space\": \"vendor-encapsulated-options-space\","
+        "    \"encapsulate\": \"\""
+        " },"
+        " {"
+        "    \"name\": \"foo2\","
+        "    \"code\": 2,"
+        "    \"type\": \"ipv4-address\","
+        "    \"array\": False,"
+        "    \"record-types\": \"\","
+        "    \"space\": \"vendor-encapsulated-options-space\","
+        "    \"encapsulate\": \"\""
+        " } ]"
+        "}";
+
+    ConstElementPtr status;
+
+    ElementPtr json = Element::fromJSON(config);
+
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
+    ASSERT_TRUE(status);
+    checkResult(status, 0);
+
+    // Once the definitions have been added we can configure the
+    // standard option #17. This option comprises an enterprise
+    // number and sub options. By convention (introduced in
+    // std_option_defs.h) option named 'vendor-opts'
+    // encapsulates the option space named 'vendor-opts-space'.
+    // We add our dummy options to this option space and thus
+    // they should be included as sub-options in the 'vendor-opts'
+    // option.
+    config = "{ \"interface\": [ \"all\" ],"
+        "\"rebind-timer\": 2000,"
+        "\"renew-timer\": 1000,"
+        "\"option-data\": [ {"
+        "    \"name\": \"vendor-encapsulated-options\","
+        "    \"space\": \"dhcp4\","
+        "    \"code\": 43,"
+        "    \"data\": \"\","
+        "    \"csv-format\": False"
+        " },"
+        " {"
+        "    \"name\": \"foo\","
+        "    \"space\": \"vendor-encapsulated-options-space\","
+        "    \"code\": 1,"
+        "    \"data\": \"1234\","
+        "    \"csv-format\": True"
+        " },"
+        " {"
+        "    \"name\": \"foo2\","
+        "    \"space\": \"vendor-encapsulated-options-space\","
+        "    \"code\": 2,"
+        "    \"data\": \"192.168.2.1\","
+        "    \"csv-format\": True"
+        " } ],"
+        "\"option-def\": [ {"
+        "    \"name\": \"foo\","
+        "    \"code\": 1,"
+        "    \"type\": \"uint32\","
+        "    \"array\": False,"
+        "    \"record-types\": \"\","
+        "    \"space\": \"vendor-encapsulated-options-space\","
+        "    \"encapsulate\": \"\""
+        " },"
+        " {"
+        "    \"name\": \"foo2\","
+        "    \"code\": 2,"
+        "    \"type\": \"ipv4-address\","
+        "    \"array\": False,"
+        "    \"record-types\": \"\","
+        "    \"space\": \"vendor-encapsulated-options-space\","
+        "    \"encapsulate\": \"\""
+        " } ],"
+        "\"subnet4\": [ { "
+        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"subnet\": \"192.0.2.0/24\""
+        " } ]"
+        "}";
+
+
+    json = Element::fromJSON(config);
+
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
+    ASSERT_TRUE(status);
+    checkResult(status, 0);
+
+    // Get the subnet.
+    Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.5"));
+    ASSERT_TRUE(subnet);
+
+    // We should have one option available.
+    Subnet::OptionContainerPtr options = subnet->getOptionDescriptors("dhcp4");
+    ASSERT_TRUE(options);
+    ASSERT_EQ(1, options->size());
+
+    // Get the option.
+    Subnet::OptionDescriptor desc =
+        subnet->getOptionDescriptor("dhcp4", DHO_VENDOR_ENCAPSULATED_OPTIONS);
+    EXPECT_TRUE(desc.option);
+    EXPECT_EQ(DHO_VENDOR_ENCAPSULATED_OPTIONS, desc.option->getType());
+
+    // Option with the code 1 should be added as a sub-option.
+    OptionPtr option_foo = desc.option->getOption(1);
+    ASSERT_TRUE(option_foo);
+    EXPECT_EQ(1, option_foo->getType());
+    // This option comprises a single uint32_t value thus it is
+    // represented by OptionInt<uint32_t> class. Let's get the
+    // object of this type.
+    boost::shared_ptr<OptionInt<uint32_t> > option_foo_uint32 =
+        boost::dynamic_pointer_cast<OptionInt<uint32_t> >(option_foo);
+    ASSERT_TRUE(option_foo_uint32);
+    // Validate the value according to the configuration.
+    EXPECT_EQ(1234, option_foo_uint32->getValue());
+
+    // Option with the code 2 should be added as a sub-option.
+    OptionPtr option_foo2 = desc.option->getOption(2);
+    ASSERT_TRUE(option_foo2);
+    EXPECT_EQ(2, option_foo2->getType());
+    // This option comprises the IPV4 address. Such option is
+    // represented by OptionCustom object.
+    OptionCustomPtr option_foo2_v4 =
+        boost::dynamic_pointer_cast<OptionCustom>(option_foo2);
+    ASSERT_TRUE(option_foo2_v4);
+    // Get the IP address carried by this option and validate it.
+    EXPECT_EQ("192.168.2.1", option_foo2_v4->readAddress().toText());
+
+    // Option with the code 3 should not be added.
+    EXPECT_FALSE(desc.option->getOption(3));
+}
+
+
 };
diff --git a/src/bin/dhcp6/config_parser.cc b/src/bin/dhcp6/config_parser.cc
index be67f69..298785b 100644
--- a/src/bin/dhcp6/config_parser.cc
+++ b/src/bin/dhcp6/config_parser.cc
@@ -102,7 +102,6 @@ OptionStorage option_defaults;
 /// @brief Global storage for option definitions.
 OptionDefStorage option_def_intermediate;
 
-
 /// @brief a dummy configuration parser
 ///
 /// This is a debugging parser. It does not configure anything,
@@ -797,7 +796,10 @@ private:
         }
 
         std::string option_space = getParam<std::string>("space", string_values_);
-        /// @todo Validate option space once #2313 is merged.
+        if (!OptionSpace::validateName(option_space)) {
+            isc_throw(DhcpConfigError, "invalid option space name'"
+                      << option_space << "'");
+        }
 
         OptionDefinitionPtr def;
         if (option_space == "dhcp6" &&
@@ -886,7 +888,7 @@ private:
             // definition of option value makes sense.
             if (def->getName() != option_name) {
                 isc_throw(DhcpConfigError, "specified option name '"
-                          << option_name << " does not match the "
+                          << option_name << "' does not match the "
                           << "option definition: '" << option_space
                           << "." << def->getName() << "'");
             }
@@ -1033,8 +1035,8 @@ public:
         BOOST_FOREACH(ConfigPair param, option_def->mapValue()) {
             std::string entry(param.first);
             ParserPtr parser;
-            if (entry == "name" || entry == "type" ||
-                entry == "record-types" || entry == "space") {
+            if (entry == "name" || entry == "type" || entry == "record-types" ||
+                entry == "space" || entry == "encapsulate") {
                 StringParserPtr
                     str_parser(dynamic_cast<StringParser*>(StringParser::factory(entry)));
                 if (str_parser) {
@@ -1084,8 +1086,8 @@ public:
 
     /// @brief Stores the parsed option definition in the data store.
     void commit() {
-        // @todo validate option space name once 2313 is merged.
-        if (storage_ && option_definition_) {
+        if (storage_ && option_definition_ &&
+            OptionSpace::validateName(option_space_name_)) {
             storage_->addItem(option_definition_, option_space_name_);
         }
     }
@@ -1107,11 +1109,10 @@ private:
     void createOptionDef() {
         // Get the option space name and validate it.
         std::string space = getParam<std::string>("space", string_values_);
-        // @todo uncomment the code below when the #2313 is merged.
-        /*        if (!OptionSpace::validateName()) {
+        if (!OptionSpace::validateName(space)) {
             isc_throw(DhcpConfigError, "invalid option space name '"
                       << space << "'");
-                      } */
+        }
 
         // Get other parameters that are needed to create the
         // option definition.
@@ -1119,9 +1120,36 @@ private:
         uint32_t code = getParam<uint32_t>("code", uint32_values_);
         std::string type = getParam<std::string>("type", string_values_);
         bool array_type = getParam<bool>("array", boolean_values_);
+        std::string encapsulates = getParam<std::string>("encapsulate",
+                                                         string_values_);
+
+        // Create option definition.
+        OptionDefinitionPtr def;
+        // We need to check if user has set encapsulated option space
+        // name. If so, different constructor will be used.
+        if (!encapsulates.empty()) {
+            // Arrays can't be used together with sub-options.
+            if (array_type) {
+                isc_throw(DhcpConfigError, "option '" << space << "."
+                          << "name" << "', comprising an array of data"
+                          << " fields may not encapsulate any option space");
+
+            } else if (encapsulates == space) {
+                isc_throw(DhcpConfigError, "option must not encapsulate"
+                          << " an option space it belongs to: '"
+                          << space << "." << name << "' is set to"
+                          << " encapsulate '" << space << "'");
+
+            } else {
+                def.reset(new OptionDefinition(name, code, type,
+                                               encapsulates.c_str()));
+            }
+
+        } else {
+            def.reset(new OptionDefinition(name, code, type, array_type));
+
+        }
 
-        OptionDefinitionPtr def(new OptionDefinition(name, code,
-                                                     type, array_type));
         // The record-types field may carry a list of comma separated names
         // of data types that form a record.
         std::string record_types = getParam<std::string>("record-types",
@@ -1139,7 +1167,7 @@ private:
                 }
             } catch (const Exception& ex) {
                 isc_throw(DhcpConfigError, "invalid record type values"
-                          << " specified for the option  definition: "
+                          << " specified for the option definition: "
                           << ex.what());
             }
         }
@@ -1358,6 +1386,63 @@ private:
         return (false);
     }
 
+    /// @brief Append sub-options to an option.
+    ///
+    /// @param option_space a name of the encapsulated option space.
+    /// @param option option instance to append sub-options to.
+    void appendSubOptions(const std::string& option_space, OptionPtr& option) {
+        // Only non-NULL options are stored in option container.
+        // If this option pointer is NULL this is a serious error.
+        assert(option);
+
+        OptionDefinitionPtr def;
+        if (option_space == "dhcp6" &&
+            LibDHCP::isStandardOption(Option::V6, option->getType())) {
+            def = LibDHCP::getOptionDef(Option::V6, option->getType());
+            // Definitions for some of the standard options hasn't been
+            // implemented so it is ok to leave here.
+            if (!def) {
+                return;
+            }
+        } else {
+            const OptionDefContainerPtr defs =
+                option_def_intermediate.getItems(option_space);
+            const OptionDefContainerTypeIndex& idx = defs->get<1>();
+            const OptionDefContainerTypeRange& range =
+                idx.equal_range(option->getType());
+            // There is no definition so we have to leave.
+            if (std::distance(range.first, range.second) == 0) {
+                return;
+            }
+
+            def = *range.first;
+
+            // If the definition exists, it must be non-NULL.
+            // Otherwise it is a programming error.
+            assert(def);
+        }
+
+        // We need to get option definition for the particular option space
+        // and code. This definition holds the information whether our
+        // option encapsulates any option space.
+        // Get the encapsulated option space name.
+        std::string encapsulated_space = def->getEncapsulatedSpace();
+        // If option space name is empty it means that our option does not
+        // encapsulate any option space (does not include sub-options).
+        if (!encapsulated_space.empty()) {
+            // Get the sub-options that belong to the encapsulated
+            // option space.
+            const Subnet::OptionContainerPtr sub_opts =
+                option_defaults.getItems(encapsulated_space);
+            // Append sub-options to the option.
+            BOOST_FOREACH(Subnet::OptionDescriptor desc, *sub_opts) {
+                if (desc.option) {
+                    option->addOption(desc.option);
+                }
+            }
+        }
+    }
+
     /// @brief Create a new subnet using a data from child parsers.
     ///
     /// @throw isc::dhcp::DhcpConfigError if subnet configuration parsing failed.
@@ -1458,6 +1543,8 @@ private:
                     LOG_WARN(dhcp6_logger, DHCP6_CONFIG_OPTION_DUPLICATE)
                         .arg(desc.option->getType()).arg(addr.toText());
                 }
+                // Add sub-options (if any).
+                appendSubOptions(option_space, desc.option);
                 // In any case, we add the option to the subnet.
                 subnet_->addOption(desc.option, false, option_space);
             }
@@ -1485,6 +1572,9 @@ private:
                 Subnet::OptionDescriptor existing_desc =
                     subnet_->getOptionDescriptor(option_space, desc.option->getType());
                 if (!existing_desc.option) {
+                    // Add sub-options (if any).
+                    appendSubOptions(option_space, desc.option);
+
                     subnet_->addOption(desc.option, false, option_space);
                 }
             }
diff --git a/src/bin/dhcp6/dhcp6.spec b/src/bin/dhcp6/dhcp6.spec
index 2799f06..a799103 100644
--- a/src/bin/dhcp6/dhcp6.spec
+++ b/src/bin/dhcp6/dhcp6.spec
@@ -86,6 +86,12 @@
             "item_type": "string",
             "item_optional": false,
             "item_default": ""
+          },
+
+          { "item_name": "encapsulate",
+            "item_type": "string",
+            "item_optional": false,
+            "item_default": ""
           } ]
         }
       },
diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc
index 1e0b4da..6ee772e 100644
--- a/src/bin/dhcp6/dhcp6_srv.cc
+++ b/src/bin/dhcp6/dhcp6_srv.cc
@@ -411,14 +411,9 @@ void Dhcpv6Srv::appendRequestedOptions(const Pkt6Ptr& question, Pkt6Ptr& answer)
     }
     // Get the list of options that client requested.
     const std::vector<uint16_t>& requested_opts = option_oro->getValues();
-    // Get the list of options configured for a subnet.
-    Subnet::OptionContainerPtr options = subnet->getOptionDescriptors("dhcp6");
-    const Subnet::OptionContainerTypeIndex& idx = options->get<1>();
-    // Try to match requested options with those configured for a subnet.
-    // If match is found, append configured option to the answer message.
     BOOST_FOREACH(uint16_t opt, requested_opts) {
-        const Subnet::OptionContainerTypeRange& range = idx.equal_range(opt);
-        BOOST_FOREACH(Subnet::OptionDescriptor desc, range) {
+        Subnet::OptionDescriptor desc = subnet->getOptionDescriptor("dhcp6", opt);
+        if (desc.option) {
             answer->addOption(desc.option);
         }
     }
diff --git a/src/bin/dhcp6/tests/config_parser_unittest.cc b/src/bin/dhcp6/tests/config_parser_unittest.cc
index b3fe88e..9c63712 100644
--- a/src/bin/dhcp6/tests/config_parser_unittest.cc
+++ b/src/bin/dhcp6/tests/config_parser_unittest.cc
@@ -18,6 +18,8 @@
 #include <dhcp/libdhcp++.h>
 #include <dhcp/option6_ia.h>
 #include <dhcp/iface_mgr.h>
+#include <dhcp/option_custom.h>
+#include <dhcp/option_int.h>
 #include <dhcp6/config_parser.h>
 #include <dhcp6/dhcp6_srv.h>
 #include <dhcpsrv/cfgmgr.h>
@@ -573,7 +575,8 @@ TEST_F(Dhcp6ParserTest, optionDefIpv6Address) {
         "      \"type\": \"ipv6-address\","
         "      \"array\": False,"
         "      \"record-types\": \"\","
-        "      \"space\": \"isc\""
+        "      \"space\": \"isc\","
+        "      \"encapsulate\": \"\""
         "  } ]"
         "}";
     ElementPtr json = Element::fromJSON(config);
@@ -611,7 +614,8 @@ TEST_F(Dhcp6ParserTest, optionDefRecord) {
         "      \"type\": \"record\","
         "      \"array\": False,"
         "      \"record-types\": \"uint16, ipv4-address, ipv6-address, string\","
-        "      \"space\": \"isc\""
+        "      \"space\": \"isc\","
+        "      \"encapsulate\": \"\""
         "  } ]"
         "}";
     ElementPtr json = Element::fromJSON(config);
@@ -658,7 +662,8 @@ TEST_F(Dhcp6ParserTest, optionDefMultiple) {
         "      \"type\": \"uint32\","
         "      \"array\": False,"
         "      \"record-types\": \"\","
-        "      \"space\": \"isc\""
+        "      \"space\": \"isc\","
+        "      \"encapsulate\": \"\""
         "  },"
         "  {"
         "      \"name\": \"foo-2\","
@@ -666,7 +671,8 @@ TEST_F(Dhcp6ParserTest, optionDefMultiple) {
         "      \"type\": \"ipv4-address\","
         "      \"array\": False,"
         "      \"record-types\": \"\","
-        "      \"space\": \"isc\""
+        "      \"space\": \"isc\","
+        "      \"encapsulate\": \"\""
         "  } ]"
         "}";
     ElementPtr json = Element::fromJSON(config);
@@ -716,7 +722,8 @@ TEST_F(Dhcp6ParserTest, optionDefDuplicate) {
         "      \"type\": \"uint32\","
         "      \"array\": False,"
         "      \"record-types\": \"\","
-        "      \"space\": \"isc\""
+        "      \"space\": \"isc\","
+        "      \"encapsulate\": \"\""
         "  },"
         "  {"
         "      \"name\": \"foo-2\","
@@ -724,7 +731,8 @@ TEST_F(Dhcp6ParserTest, optionDefDuplicate) {
         "      \"type\": \"ipv4-address\","
         "      \"array\": False,"
         "      \"record-types\": \"\","
-        "      \"space\": \"isc\""
+        "      \"space\": \"isc\","
+        "      \"encapsulate\": \"\""
         "  } ]"
         "}";
     ElementPtr json = Element::fromJSON(config);
@@ -752,7 +760,8 @@ TEST_F(Dhcp6ParserTest, optionDefArray) {
         "      \"type\": \"uint32\","
         "      \"array\": True,"
         "      \"record-types\": \"\","
-        "      \"space\": \"isc\""
+        "      \"space\": \"isc\","
+        "      \"encapsulate\": \"\""
         "  } ]"
         "}";
     ElementPtr json = Element::fromJSON(config);
@@ -778,6 +787,47 @@ TEST_F(Dhcp6ParserTest, optionDefArray) {
     EXPECT_TRUE(def->getArrayType());
 }
 
+// The purpose of this test to verify that encapsulated option
+// space name may be specified.
+TEST_F(Dhcp6ParserTest, optionDefEncapsulate) {
+
+    // Configuration string. Included the encapsulated
+    // option space name.
+    std::string config =
+        "{ \"option-def\": [ {"
+        "      \"name\": \"foo\","
+        "      \"code\": 100,"
+        "      \"type\": \"uint32\","
+        "      \"array\": False,"
+        "      \"record-types\": \"\","
+        "      \"space\": \"isc\","
+        "      \"encapsulate\": \"sub-opts-space\""
+        "  } ]"
+        "}";
+    ElementPtr json = Element::fromJSON(config);
+
+    // Make sure that the particular option definition does not exist.
+    OptionDefinitionPtr def = CfgMgr::instance().getOptionDef("isc", 100);
+    ASSERT_FALSE(def);
+
+    // Use the configuration string to create new option definition.
+    ConstElementPtr status;
+    EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
+    ASSERT_TRUE(status);
+    checkResult(status, 0);
+
+    // The option definition should now be available in the CfgMgr.
+    def = CfgMgr::instance().getOptionDef("isc", 100);
+    ASSERT_TRUE(def);
+
+    // Check the option data.
+    EXPECT_EQ("foo", def->getName());
+    EXPECT_EQ(100, def->getCode());
+    EXPECT_EQ(OPT_UINT32_TYPE, def->getType());
+    EXPECT_FALSE(def->getArrayType());
+    EXPECT_EQ("sub-opts-space", def->getEncapsulatedSpace());
+}
+
 /// The purpose of this test is to verify that the option definition
 /// with invalid name is not accepted.
 TEST_F(Dhcp6ParserTest, optionDefInvalidName) {
@@ -790,7 +840,8 @@ TEST_F(Dhcp6ParserTest, optionDefInvalidName) {
         "      \"type\": \"string\","
         "      \"array\": False,"
         "      \"record-types\": \"\","
-        "      \"space\": \"isc\""
+        "      \"space\": \"isc\","
+        "      \"encapsulate\": \"\""
         "  } ]"
         "}";
     ElementPtr json = Element::fromJSON(config);
@@ -815,7 +866,8 @@ TEST_F(Dhcp6ParserTest, optionDefInvalidType) {
         "      \"type\": \"sting\","
         "      \"array\": False,"
         "      \"record-types\": \"\","
-        "      \"space\": \"isc\""
+        "      \"space\": \"isc\","
+        "      \"encapsulate\": \"\""
         "  } ]"
         "}";
     ElementPtr json = Element::fromJSON(config);
@@ -840,7 +892,8 @@ TEST_F(Dhcp6ParserTest, optionDefInvalidRecordType) {
         "      \"type\": \"record\","
         "      \"array\": False,"
         "      \"record-types\": \"uint32,uint8,sting\","
-        "      \"space\": \"isc\""
+        "      \"space\": \"isc\","
+        "      \"encapsulate\": \"\""
         "  } ]"
         "}";
     ElementPtr json = Element::fromJSON(config);
@@ -853,6 +906,85 @@ TEST_F(Dhcp6ParserTest, optionDefInvalidRecordType) {
     checkResult(status, 1);
 }
 
+/// The goal of this test is to verify that the invalid encapsulated
+/// option space name is not accepted.
+TEST_F(Dhcp6ParserTest, optionDefInvalidEncapsulatedSpace) {
+    // Configuration string. The encapsulated option space
+    // name is invalid (% character is not allowed).
+    std::string config =
+        "{ \"option-def\": [ {"
+        "      \"name\": \"foo\","
+        "      \"code\": 100,"
+        "      \"type\": \"uint32\","
+        "      \"array\": False,"
+        "      \"record-types\": \"\","
+        "      \"space\": \"isc\","
+        "      \"encapsulate\": \"invalid%space%name\""
+        "  } ]"
+        "}";
+    ElementPtr json = Element::fromJSON(config);
+
+    // Use the configuration string to create new option definition.
+    ConstElementPtr status;
+    EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
+    ASSERT_TRUE(status);
+    // Expecting parsing error (error code 1).
+    checkResult(status, 1);
+}
+
+/// The goal of this test is to verify that the encapsulated
+/// option space name can't be specified for the option that
+/// comprises an array of data fields.
+TEST_F(Dhcp6ParserTest, optionDefEncapsulatedSpaceAndArray) {
+    // Configuration string. The encapsulated option space
+    // name is set to non-empty value and the array flag
+    // is set.
+    std::string config =
+        "{ \"option-def\": [ {"
+        "      \"name\": \"foo\","
+        "      \"code\": 100,"
+        "      \"type\": \"uint32\","
+        "      \"array\": True,"
+        "      \"record-types\": \"\","
+        "      \"space\": \"isc\","
+        "      \"encapsulate\": \"valid-space-name\""
+        "  } ]"
+        "}";
+    ElementPtr json = Element::fromJSON(config);
+
+    // Use the configuration string to create new option definition.
+    ConstElementPtr status;
+    EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
+    ASSERT_TRUE(status);
+    // Expecting parsing error (error code 1).
+    checkResult(status, 1);
+}
+
+/// The goal of this test is to verify that the option may not
+/// encapsulate option space it belongs to.
+TEST_F(Dhcp6ParserTest, optionDefEncapsulateOwnSpace) {
+    // Configuration string. Option is set to encapsulate
+    // option space it belongs to.
+    std::string config =
+        "{ \"option-def\": [ {"
+        "      \"name\": \"foo\","
+        "      \"code\": 100,"
+        "      \"type\": \"uint32\","
+        "      \"array\": False,"
+        "      \"record-types\": \"\","
+        "      \"space\": \"isc\","
+        "      \"encapsulate\": \"isc\""
+        "  } ]"
+        "}";
+    ElementPtr json = Element::fromJSON(config);
+
+    // Use the configuration string to create new option definition.
+    ConstElementPtr status;
+    EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
+    ASSERT_TRUE(status);
+    // Expecting parsing error (error code 1).
+    checkResult(status, 1);
+}
 
 /// The purpose of this test is to verify that it is not allowed
 /// to override the standard option (that belongs to dhcp6 option
@@ -871,7 +1003,8 @@ TEST_F(Dhcp6ParserTest, optionStandardDefOverride) {
         "      \"type\": \"string\","
         "      \"array\": False,"
         "      \"record-types\": \"\","
-        "      \"space\": \"dhcp6\""
+        "      \"space\": \"dhcp6\","
+        "      \"encapsulate\": \"\""
         "  } ]"
         "}";
     ElementPtr json = Element::fromJSON(config);
@@ -906,7 +1039,8 @@ TEST_F(Dhcp6ParserTest, optionStandardDefOverride) {
         "      \"type\": \"string\","
         "      \"array\": False,"
         "      \"record-types\": \"\","
-        "      \"space\": \"dhcp6\""
+        "      \"space\": \"dhcp6\","
+        "      \"encapsulate\": \"\""
         "  } ]"
         "}";
     json = Element::fromJSON(config);
@@ -1028,7 +1162,8 @@ TEST_F(Dhcp6ParserTest, optionDataTwoSpaces) {
         "    \"type\": \"uint32\","
         "    \"array\": False,"
         "    \"record-types\": \"\","
-        "    \"space\": \"isc\""
+        "    \"space\": \"isc\","
+        "    \"encapsulate\": \"\""
         " } ],"
         "\"subnet6\": [ { "
         "    \"pool\": [ \"2001:db8:1::/80\" ],"
@@ -1061,81 +1196,164 @@ TEST_F(Dhcp6ParserTest, optionDataTwoSpaces) {
     ASSERT_FALSE(desc3.option);
 }
 
-// The goal of this test is to verify options configuration
-// for a single subnet. In particular this test checks
-// that local options configuration overrides global
-// option setting.
-TEST_F(Dhcp6ParserTest, optionDataInSingleSubnet) {
-    ConstElementPtr x;
+// The goal of this test is to verify that it is possible to
+// encapsulate option space containing some options with
+// another option. In this test we create base option that
+// encapsulates option space 'isc' that comprises two other
+// options. Also, for all options their definitions are
+// created.
+TEST_F(Dhcp6ParserTest, optionDataEncapsulate) {
+
+    // @todo DHCP configurations has many dependencies between
+    // parameters. First of all, configuration for subnet is
+    // inherited from the global values. Thus subnet has to be
+    // configured when all global values have been configured.
+    // Also, an option can encapsulate another option only
+    // if the latter has been configured. For this reason in this
+    // test we created two-stage configuration where first we
+    // created options that belong to encapsulated option space.
+    // In the second stage we add the base option. Also, the Subnet
+    // object is configured in the second stage so it is created
+    // at the very end (when all other parameters are configured).
+
+    // Starting stage 1. Configure sub-options and their definitions.
     string config = "{ \"interface\": [ \"all\" ],"
-        "\"preferred-lifetime\": 3000,"
-        "\"rebind-timer\": 2000, "
-        "\"renew-timer\": 1000, "
+        "\"rebind-timer\": 2000,"
+        "\"renew-timer\": 1000,"
         "\"option-data\": [ {"
-        "      \"name\": \"subscriber-id\","
-        "      \"space\": \"dhcp6\","
-        "      \"code\": 38,"
-        "      \"data\": \"AB\","
-        "      \"csv-format\": False"
+        "    \"name\": \"foo\","
+        "    \"space\": \"isc\","
+        "    \"code\": 110,"
+        "    \"data\": \"1234\","
+        "    \"csv-format\": True"
+        " },"
+        " {"
+        "    \"name\": \"foo2\","
+        "    \"space\": \"isc\","
+        "    \"code\": 111,"
+        "    \"data\": \"192.168.2.1\","
+        "    \"csv-format\": True"
+        " } ],"
+        "\"option-def\": [ {"
+        "    \"name\": \"foo\","
+        "    \"code\": 110,"
+        "    \"type\": \"uint32\","
+        "    \"array\": False,"
+        "    \"record-types\": \"\","
+        "    \"space\": \"isc\","
+        "    \"encapsulate\": \"\""
+        " },"
+        " {"
+        "    \"name\": \"foo2\","
+        "    \"code\": 111,"
+        "    \"type\": \"ipv4-address\","
+        "    \"array\": False,"
+        "    \"record-types\": \"\","
+        "    \"space\": \"isc\","
+        "    \"encapsulate\": \"\""
+        " } ]"
+        "}";
+
+    ConstElementPtr status;
+
+    ElementPtr json = Element::fromJSON(config);
+
+    EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
+    ASSERT_TRUE(status);
+    checkResult(status, 0);
+
+    // Stage 2. Configure base option and a subnet. Please note that
+    // the configuration from the stage 2 is repeated because BIND
+    // configuration manager sends whole configuration for the lists
+    // where at least one element is being modified or added.
+    config = "{ \"interface\": [ \"all\" ],"
+        "\"rebind-timer\": 2000,"
+        "\"renew-timer\": 1000,"
+        "\"option-data\": [ {"
+        "    \"name\": \"base-option\","
+        "    \"space\": \"dhcp6\","
+        "    \"code\": 100,"
+        "    \"data\": \"11\","
+        "    \"csv-format\": True"
+        " },"
+        " {"
+        "    \"name\": \"foo\","
+        "    \"space\": \"isc\","
+        "    \"code\": 110,"
+        "    \"data\": \"1234\","
+        "    \"csv-format\": True"
+        " },"
+        " {"
+        "    \"name\": \"foo2\","
+        "    \"space\": \"isc\","
+        "    \"code\": 111,"
+        "    \"data\": \"192.168.2.1\","
+        "    \"csv-format\": True"
+        " } ],"
+        "\"option-def\": [ {"
+        "    \"name\": \"base-option\","
+        "    \"code\": 100,"
+        "    \"type\": \"uint8\","
+        "    \"array\": False,"
+        "    \"record-types\": \"\","
+        "    \"space\": \"dhcp6\","
+        "    \"encapsulate\": \"isc\""
+        "},"
+        "{"
+        "    \"name\": \"foo\","
+        "    \"code\": 110,"
+        "    \"type\": \"uint32\","
+        "    \"array\": False,"
+        "    \"record-types\": \"\","
+        "    \"space\": \"isc\","
+        "    \"encapsulate\": \"\""
+        " },"
+        " {"
+        "    \"name\": \"foo2\","
+        "    \"code\": 111,"
+        "    \"type\": \"ipv4-address\","
+        "    \"array\": False,"
+        "    \"record-types\": \"\","
+        "    \"space\": \"isc\","
+        "    \"encapsulate\": \"\""
         " } ],"
         "\"subnet6\": [ { "
         "    \"pool\": [ \"2001:db8:1::/80\" ],"
-        "    \"subnet\": \"2001:db8:1::/64\", "
-        "    \"option-data\": [ {"
-        "          \"name\": \"subscriber-id\","
-        "          \"space\": \"dhcp6\","
-        "          \"code\": 38,"
-        "          \"data\": \"AB CDEF0105\","
-        "          \"csv-format\": False"
-        "        },"
-        "        {"
-        "          \"name\": \"preference\","
-        "          \"space\": \"dhcp6\","
-        "          \"code\": 7,"
-        "          \"data\": \"01\","
-        "          \"csv-format\": False"
-        "        } ]"
-        " } ],"
-        "\"valid-lifetime\": 4000 }";
+        "    \"subnet\": \"2001:db8:1::/64\""
+        " } ]"
+        "}";
 
-    ElementPtr json = Element::fromJSON(config);
 
-    EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
-    ASSERT_TRUE(x);
-    comment_ = parseAnswer(rcode_, x);
-    ASSERT_EQ(0, rcode_);
+    json = Element::fromJSON(config);
 
+    EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
+    ASSERT_TRUE(status);
+    checkResult(status, 0);
+
+    // Get the subnet.
     Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"));
     ASSERT_TRUE(subnet);
-    Subnet::OptionContainerPtr options = subnet->getOptionDescriptors("dhcp6");
-    ASSERT_EQ(2, options->size());
 
-    // Get the search index. Index #1 is to search using option code.
-    const Subnet::OptionContainerTypeIndex& idx = options->get<1>();
-
-    // Get the options for specified index. Expecting one option to be
-    // returned but in theory we may have multiple options with the same
-    // code so we get the range.
-    std::pair<Subnet::OptionContainerTypeIndex::const_iterator,
-              Subnet::OptionContainerTypeIndex::const_iterator> range =
-        idx.equal_range(D6O_SUBSCRIBER_ID);
-    // Expect single option with the code equal to 38.
-    ASSERT_EQ(1, std::distance(range.first, range.second));
-    const uint8_t subid_expected[] = {
-        0xAB, 0xCD, 0xEF, 0x01, 0x05
-    };
-    // Check if option is valid in terms of code and carried data.
-    testOption(*range.first, D6O_SUBSCRIBER_ID, subid_expected,
-               sizeof(subid_expected));
+    // We should have one option available.
+    Subnet::OptionContainerPtr options = subnet->getOptionDescriptors("dhcp6");
+    ASSERT_TRUE(options);
+    ASSERT_EQ(1, options->size());
 
-    range = idx.equal_range(D6O_PREFERENCE);
-    ASSERT_EQ(1, std::distance(range.first, range.second));
-    // Do another round of testing with second option.
-    const uint8_t pref_expected[] = {
-        0x01
-    };
-    testOption(*range.first, D6O_PREFERENCE, pref_expected,
-               sizeof(pref_expected));
+    // Get the option.
+    Subnet::OptionDescriptor desc = subnet->getOptionDescriptor("dhcp6", 100);
+    EXPECT_TRUE(desc.option);
+    EXPECT_EQ(100, desc.option->getType());
+
+    // This opton should comprise two sub-options.
+    // Onf of them is 'foo' with code 110.
+    OptionPtr option_foo = desc.option->getOption(110);
+    ASSERT_TRUE(option_foo);
+    EXPECT_EQ(110, option_foo->getType());
+
+    // ...another one 'foo2' with code 111.
+    OptionPtr option_foo2 = desc.option->getOption(111);
+    ASSERT_TRUE(option_foo2);
+    EXPECT_EQ(111, option_foo2->getType());
 }
 
 // Goal of this test is to verify options configuration
@@ -1377,4 +1595,164 @@ TEST_F(Dhcp6ParserTest, stdOptionData) {
     EXPECT_EQ(1516, optionIA->getT2());
 }
 
+// The goal of this test is to verify that the standard option can
+// be configured to encapsulate multiple other options.
+TEST_F(Dhcp6ParserTest, stdOptionDataEncapsulate) {
+
+    // The configuration is two stage process in this test.
+    // In the first stahe we create definitions of suboptions
+    // that we will add to the base option.
+    // Let's create some dummy options: foo and foo2.
+    string config = "{ \"interface\": [ \"all\" ],"
+        "\"rebind-timer\": 2000,"
+        "\"renew-timer\": 1000,"
+        "\"option-data\": [ {"
+        "    \"name\": \"foo\","
+        "    \"space\": \"vendor-opts-space\","
+        "    \"code\": 110,"
+        "    \"data\": \"1234\","
+        "    \"csv-format\": True"
+        " },"
+        " {"
+        "    \"name\": \"foo2\","
+        "    \"space\": \"vendor-opts-space\","
+        "    \"code\": 111,"
+        "    \"data\": \"192.168.2.1\","
+        "    \"csv-format\": True"
+        " } ],"
+        "\"option-def\": [ {"
+        "    \"name\": \"foo\","
+        "    \"code\": 110,"
+        "    \"type\": \"uint32\","
+        "    \"array\": False,"
+        "    \"record-types\": \"\","
+        "    \"space\": \"vendor-opts-space\","
+        "    \"encapsulate\": \"\""
+        " },"
+        " {"
+        "    \"name\": \"foo2\","
+        "    \"code\": 111,"
+        "    \"type\": \"ipv4-address\","
+        "    \"array\": False,"
+        "    \"record-types\": \"\","
+        "    \"space\": \"vendor-opts-space\","
+        "    \"encapsulate\": \"\""
+        " } ]"
+        "}";
+
+    ConstElementPtr status;
+
+    ElementPtr json = Element::fromJSON(config);
+
+    EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
+    ASSERT_TRUE(status);
+    checkResult(status, 0);
+
+    // Once the definitions have been added we can configure the
+    // standard option #17. This option comprises an enterprise
+    // number and sub options. By convention (introduced in
+    // std_option_defs.h) option named 'vendor-opts'
+    // encapsulates the option space named 'vendor-opts-space'.
+    // We add our dummy options to this option space and thus
+    // they should be included as sub-options in the 'vendor-opts'
+    // option.
+    config = "{ \"interface\": [ \"all\" ],"
+        "\"rebind-timer\": 2000,"
+        "\"renew-timer\": 1000,"
+        "\"option-data\": [ {"
+        "    \"name\": \"vendor-opts\","
+        "    \"space\": \"dhcp6\","
+        "    \"code\": 17,"
+        "    \"data\": \"1234\","
+        "    \"csv-format\": True"
+        " },"
+        " {"
+        "    \"name\": \"foo\","
+        "    \"space\": \"vendor-opts-space\","
+        "    \"code\": 110,"
+        "    \"data\": \"1234\","
+        "    \"csv-format\": True"
+        " },"
+        " {"
+        "    \"name\": \"foo2\","
+        "    \"space\": \"vendor-opts-space\","
+        "    \"code\": 111,"
+        "    \"data\": \"192.168.2.1\","
+        "    \"csv-format\": True"
+        " } ],"
+        "\"option-def\": [ {"
+        "    \"name\": \"foo\","
+        "    \"code\": 110,"
+        "    \"type\": \"uint32\","
+        "    \"array\": False,"
+        "    \"record-types\": \"\","
+        "    \"space\": \"vendor-opts-space\","
+        "    \"encapsulate\": \"\""
+        " },"
+        " {"
+        "    \"name\": \"foo2\","
+        "    \"code\": 111,"
+        "    \"type\": \"ipv4-address\","
+        "    \"array\": False,"
+        "    \"record-types\": \"\","
+        "    \"space\": \"vendor-opts-space\","
+        "    \"encapsulate\": \"\""
+        " } ],"
+        "\"subnet6\": [ { "
+        "    \"pool\": [ \"2001:db8:1::/80\" ],"
+        "    \"subnet\": \"2001:db8:1::/64\""
+        " } ]"
+        "}";
+
+
+    json = Element::fromJSON(config);
+
+    EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
+    ASSERT_TRUE(status);
+    checkResult(status, 0);
+
+    // Get the subnet.
+    Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"));
+    ASSERT_TRUE(subnet);
+
+    // We should have one option available.
+    Subnet::OptionContainerPtr options = subnet->getOptionDescriptors("dhcp6");
+    ASSERT_TRUE(options);
+    ASSERT_EQ(1, options->size());
+
+    // Get the option.
+    Subnet::OptionDescriptor desc =
+        subnet->getOptionDescriptor("dhcp6", D6O_VENDOR_OPTS);
+    EXPECT_TRUE(desc.option);
+    EXPECT_EQ(D6O_VENDOR_OPTS, desc.option->getType());
+
+    // Option with the code 110 should be added as a sub-option.
+    OptionPtr option_foo = desc.option->getOption(110);
+    ASSERT_TRUE(option_foo);
+    EXPECT_EQ(110, option_foo->getType());
+    // This option comprises a single uint32_t value thus it is
+    // represented by OptionInt<uint32_t> class. Let's get the
+    // object of this type.
+    boost::shared_ptr<OptionInt<uint32_t> > option_foo_uint32 =
+        boost::dynamic_pointer_cast<OptionInt<uint32_t> >(option_foo);
+    ASSERT_TRUE(option_foo_uint32);
+    // Validate the value according to the configuration.
+    EXPECT_EQ(1234, option_foo_uint32->getValue());
+
+    // Option with the code 111 should be added as a sub-option.
+    OptionPtr option_foo2 = desc.option->getOption(111);
+    ASSERT_TRUE(option_foo2);
+    EXPECT_EQ(111, option_foo2->getType());
+    // This option comprises the IPV4 address. Such option is
+    // represented by OptionCustom object.
+    OptionCustomPtr option_foo2_v4 =
+        boost::dynamic_pointer_cast<OptionCustom>(option_foo2);
+    ASSERT_TRUE(option_foo2_v4);
+    // Get the IP address carried by this option and validate it.
+    EXPECT_EQ("192.168.2.1", option_foo2_v4->readAddress().toText());
+
+    // Option with the code 112 should not be added.
+    EXPECT_FALSE(desc.option->getOption(112));
+}
+
 };
diff --git a/src/lib/dhcp/Makefile.am b/src/lib/dhcp/Makefile.am
index 822c4e3..f169fe6 100644
--- a/src/lib/dhcp/Makefile.am
+++ b/src/lib/dhcp/Makefile.am
@@ -32,6 +32,7 @@ libb10_dhcp___la_SOURCES += option.cc option.h
 libb10_dhcp___la_SOURCES += option_custom.cc option_custom.h
 libb10_dhcp___la_SOURCES += option_data_types.cc option_data_types.h
 libb10_dhcp___la_SOURCES += option_definition.cc option_definition.h
+libb10_dhcp___la_SOURCES += option_space.cc option_space.h
 libb10_dhcp___la_SOURCES += pkt6.cc pkt6.h
 libb10_dhcp___la_SOURCES += pkt4.cc pkt4.h
 libb10_dhcp___la_SOURCES += std_option_defs.h
diff --git a/src/lib/dhcp/libdhcp++.cc b/src/lib/dhcp/libdhcp++.cc
index a3921cc..1484458 100644
--- a/src/lib/dhcp/libdhcp++.cc
+++ b/src/lib/dhcp/libdhcp++.cc
@@ -329,10 +329,35 @@ LibDHCP::initStdOptionDefs4() {
 
     // Now let's add all option definitions.
     for (int i = 0; i < OPTION_DEF_PARAMS_SIZE4; ++i) {
-        OptionDefinitionPtr definition(new OptionDefinition(OPTION_DEF_PARAMS4[i].name,
-                                                            OPTION_DEF_PARAMS4[i].code,
-                                                            OPTION_DEF_PARAMS4[i].type,
-                                                            OPTION_DEF_PARAMS4[i].array));
+        std::string encapsulates(OPTION_DEF_PARAMS4[i].encapsulates);
+        if (!encapsulates.empty() && OPTION_DEF_PARAMS4[i].array) {
+            isc_throw(isc::BadValue, "invalid standard option definition: "
+                      << "option with code '" << OPTION_DEF_PARAMS4[i].code
+                      << "' may not encapsulate option space '"
+                      << encapsulates << "' because the definition"
+                      << " indicates that this option comprises an array"
+                      << " of values");
+        }
+
+        // Depending whether the option encapsulates an option space or not
+        // we pick different constructor to create an instance of the option
+        // definition.
+        OptionDefinitionPtr definition;
+        if (encapsulates.empty()) {
+            // Option does not encapsulate any option space.
+            definition.reset(new OptionDefinition(OPTION_DEF_PARAMS4[i].name,
+                                                  OPTION_DEF_PARAMS4[i].code,
+                                                  OPTION_DEF_PARAMS4[i].type,
+                                                  OPTION_DEF_PARAMS4[i].array));
+
+        } else {
+            // Option does encapsulate an option space.
+            definition.reset(new OptionDefinition(OPTION_DEF_PARAMS4[i].name,
+                                                  OPTION_DEF_PARAMS4[i].code,
+                                                  OPTION_DEF_PARAMS4[i].type,
+                                                  OPTION_DEF_PARAMS4[i].encapsulates));
+
+        }
 
         for (int rec = 0; rec < OPTION_DEF_PARAMS4[i].records_size; ++rec) {
             definition->addRecordField(OPTION_DEF_PARAMS4[i].records[rec]);
@@ -358,10 +383,34 @@ LibDHCP::initStdOptionDefs6() {
     v6option_defs_.clear();
 
     for (int i = 0; i < OPTION_DEF_PARAMS_SIZE6; ++i) {
-        OptionDefinitionPtr definition(new OptionDefinition(OPTION_DEF_PARAMS6[i].name,
-                                                            OPTION_DEF_PARAMS6[i].code,
-                                                            OPTION_DEF_PARAMS6[i].type,
-                                                            OPTION_DEF_PARAMS6[i].array));
+        std::string encapsulates(OPTION_DEF_PARAMS6[i].encapsulates);
+        if (!encapsulates.empty() && OPTION_DEF_PARAMS6[i].array) {
+            isc_throw(isc::BadValue, "invalid standard option definition: "
+                      << "option with code '" << OPTION_DEF_PARAMS6[i].code
+                      << "' may not encapsulate option space '"
+                      << encapsulates << "' because the definition"
+                      << " indicates that this option comprises an array"
+                      << " of values");
+        }
+
+        // Depending whether an option encapsulates an option space or not
+        // we pick different constructor to create an instance of the option
+        // definition.
+        OptionDefinitionPtr definition;
+        if (encapsulates.empty()) {
+            // Option does not encapsulate any option space.
+            definition.reset(new OptionDefinition(OPTION_DEF_PARAMS6[i].name,
+                                                  OPTION_DEF_PARAMS6[i].code,
+                                                  OPTION_DEF_PARAMS6[i].type,
+                                                  OPTION_DEF_PARAMS6[i].array));
+        } else {
+            // Option does encapsulate an option space.
+            definition.reset(new OptionDefinition(OPTION_DEF_PARAMS6[i].name,
+                                                  OPTION_DEF_PARAMS6[i].code,
+                                                  OPTION_DEF_PARAMS6[i].type,
+                                                  OPTION_DEF_PARAMS6[i].encapsulates));
+
+        }
 
         for (int rec = 0; rec < OPTION_DEF_PARAMS6[i].records_size; ++rec) {
             definition->addRecordField(OPTION_DEF_PARAMS6[i].records[rec]);
diff --git a/src/lib/dhcp/option_custom.h b/src/lib/dhcp/option_custom.h
index 0ee4688..efb59ab 100644
--- a/src/lib/dhcp/option_custom.h
+++ b/src/lib/dhcp/option_custom.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-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
@@ -352,6 +352,9 @@ private:
     std::vector<OptionBuffer> buffers_;
 };
 
+/// A pointer to the OptionCustom object.
+typedef boost::shared_ptr<OptionCustom> OptionCustomPtr;
+
 } // namespace isc::dhcp
 } // namespace isc
 
diff --git a/src/lib/dhcp/option_definition.cc b/src/lib/dhcp/option_definition.cc
index d2b5aae..a1e396e 100644
--- a/src/lib/dhcp/option_definition.cc
+++ b/src/lib/dhcp/option_definition.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-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
@@ -21,6 +21,7 @@
 #include <dhcp/option_definition.h>
 #include <dhcp/option_int.h>
 #include <dhcp/option_int_array.h>
+#include <dhcp/option_space.h>
 #include <util/encode/hex.h>
 #include <util/strutil.h>
 #include <boost/algorithm/string/classification.hpp>
@@ -40,7 +41,8 @@ OptionDefinition::OptionDefinition(const std::string& name,
     : name_(name),
       code_(code),
       type_(OPT_UNKNOWN_TYPE),
-      array_type_(array_type) {
+      array_type_(array_type),
+      encapsulated_space_("") {
     // Data type is held as enum value by this class.
     // Use the provided option type string to get the
     // corresponding enum value.
@@ -54,7 +56,33 @@ OptionDefinition::OptionDefinition(const std::string& name,
     : name_(name),
       code_(code),
       type_(type),
-      array_type_(array_type) {
+      array_type_(array_type),
+      encapsulated_space_("") {
+}
+
+OptionDefinition::OptionDefinition(const std::string& name,
+                                   const uint16_t code,
+                                   const std::string& type,
+                                   const char* encapsulated_space)
+    : name_(name),
+      code_(code),
+      // Data type is held as enum value by this class.
+      // Use the provided option type string to get the
+      // corresponding enum value.
+      type_(OptionDataTypeUtil::getDataType(type)),
+      array_type_(false),
+      encapsulated_space_(encapsulated_space) {
+}
+
+OptionDefinition::OptionDefinition(const std::string& name,
+                                   const uint16_t code,
+                                   const OptionDataType type,
+                                   const char* encapsulated_space)
+    : name_(name),
+      code_(code),
+      type_(type),
+      array_type_(false),
+      encapsulated_space_(encapsulated_space) {
 }
 
 void
@@ -228,6 +256,11 @@ OptionDefinition::validate() const {
         all(find_tail(name_, 1), boost::is_any_of(std::string("-_")))) {
         err_str << "invalid option name '" << name_ << "'";
 
+    } else if (!encapsulated_space_.empty() &&
+               !OptionSpace::validateName(encapsulated_space_)) {
+        err_str << "invalid encapsulated option space name: '"
+                << encapsulated_space_ << "'";
+
     } else if (type_ >= OPT_UNKNOWN_TYPE) {
         // Option definition must be of a known type.
         err_str << "option type value " << type_ << " is out of range.";
diff --git a/src/lib/dhcp/option_definition.h b/src/lib/dhcp/option_definition.h
index efcaba0..df5def7 100644
--- a/src/lib/dhcp/option_definition.h
+++ b/src/lib/dhcp/option_definition.h
@@ -146,10 +146,10 @@ public:
     /// @param type option data type as string.
     /// @param array_type array indicator, if true it indicates that the
     /// option fields are the array.
-    OptionDefinition(const std::string& name,
-                     const uint16_t code,
-                     const std::string& type,
-                     const bool array_type = false);
+    explicit OptionDefinition(const std::string& name,
+                              const uint16_t code,
+                              const std::string& type,
+                              const bool array_type = false);
 
     /// @brief Constructor.
     ///
@@ -158,10 +158,49 @@ public:
     /// @param type option data type.
     /// @param array_type array indicator, if true it indicates that the
     /// option fields are the array.
-    OptionDefinition(const std::string& name,
-                     const uint16_t code,
-                     const OptionDataType type,
-                     const bool array_type = false);
+    explicit OptionDefinition(const std::string& name,
+                              const uint16_t code,
+                              const OptionDataType type,
+                              const bool array_type = false);
+
+    /// @brief Constructor.
+    ///
+    /// This constructor sets the name of the option space that is
+    /// encapsulated by this option. The encapsulated option space
+    /// indentifies sub-options that are carried within this option.
+    /// This constructor does not allow to set array indicator
+    /// because options comprising an array of data fields must
+    /// not be used with sub-options.
+    ///
+    /// @param name option name.
+    /// @param code option code.
+    /// @param type option data type given as string.
+    /// @param encapsulated_space name of the option space being
+    /// encapsulated by this option.
+    explicit OptionDefinition(const std::string& name,
+                              const uint16_t code,
+                              const std::string& type,
+                              const char* encapsulated_space);
+
+    /// @brief Constructor.
+    ///
+    /// This constructor sets the name of the option space that is
+    /// encapsulated by this option. The encapsulated option space
+    /// indentifies sub-options that are carried within this option.
+    /// This constructor does not allow to set array indicator
+    /// because options comprising an array of data fields must
+    /// not be used with sub-options.
+    ///
+    /// @param name option name.
+    /// @param code option code.
+    /// @param type option data type.
+    /// @param encapsulated_space name of the option space being
+    /// encapsulated by this option.
+    explicit OptionDefinition(const std::string& name,
+                              const uint16_t code,
+                              const OptionDataType type,
+                              const char* encapsulated_space);
+
 
     /// @brief Adds data field to the record.
     ///
@@ -192,10 +231,17 @@ public:
     /// @return option code.
     uint16_t getCode() const { return (code_); }
 
+    /// @brief Return name of the encapsulated option space.
+    ///
+    /// @return name of the encapsulated option space.
+    std::string getEncapsulatedSpace() const {
+        return (encapsulated_space_);
+    }
+
     /// @brief Return option name.
     ///
     /// @return option name.
-    const std::string& getName() const { return (name_); }
+    std::string getName() const { return (name_); }
 
     /// @brief Return list of record fields.
     ///
@@ -456,6 +502,8 @@ private:
     OptionDataType type_;
     /// Indicates wheter option is a single value or array.
     bool array_type_;
+    /// Name of the space being encapsulated by this option.
+    std::string encapsulated_space_;
     /// Collection of data fields within the record.
     RecordFieldsCollection record_fields_;
 };
diff --git a/src/lib/dhcp/option_space.cc b/src/lib/dhcp/option_space.cc
new file mode 100644
index 0000000..f9f5bee
--- /dev/null
+++ b/src/lib/dhcp/option_space.cc
@@ -0,0 +1,71 @@
+// Copyright (C) 2012, 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 <dhcp/option_space.h>
+#include <boost/algorithm/string/classification.hpp>
+#include <boost/algorithm/string/predicate.hpp>
+
+namespace isc {
+namespace dhcp {
+
+OptionSpace::OptionSpace(const std::string& name, const bool vendor_space)
+    : name_(name), vendor_space_(vendor_space) {
+    //  Check that provided option space name is valid.
+    if (!validateName(name_)) {
+        isc_throw(InvalidOptionSpace, "Invalid option space name "
+                  << name_);
+    }
+}
+
+bool
+OptionSpace::validateName(const std::string& name) {
+
+    using namespace boost::algorithm;
+
+    // Allowed characters are: lower or upper case letters, digits,
+    // underscores and hyphens. Empty option spaces are not allowed.
+    if (all(name, boost::is_from_range('a', 'z') ||
+            boost::is_from_range('A', 'Z') ||
+            boost::is_digit() ||
+            boost::is_any_of(std::string("-_"))) &&
+        !name.empty() &&
+        // Hyphens and underscores are not allowed at the beginning
+        // and at the end of the option space name.
+        !all(find_head(name, 1), boost::is_any_of(std::string("-_"))) &&
+        !all(find_tail(name, 1), boost::is_any_of(std::string("-_")))) {
+        return (true);
+
+    }
+    return (false);
+}
+
+OptionSpace6::OptionSpace6(const std::string& name)
+    : OptionSpace(name),
+      enterprise_number_(0) {
+}
+
+OptionSpace6::OptionSpace6(const std::string& name,
+                           const uint32_t enterprise_number)
+    : OptionSpace(name, true),
+      enterprise_number_(enterprise_number) {
+}
+
+void
+OptionSpace6::setVendorSpace(const uint32_t enterprise_number) {
+    enterprise_number_ = enterprise_number;
+    OptionSpace::setVendorSpace();
+}
+
+} // end of isc::dhcp namespace
+} // end of isc namespace
diff --git a/src/lib/dhcp/option_space.h b/src/lib/dhcp/option_space.h
new file mode 100644
index 0000000..9eebd76
--- /dev/null
+++ b/src/lib/dhcp/option_space.h
@@ -0,0 +1,189 @@
+// Copyright (C) 2012, 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 OPTION_SPACE_H
+#define OPTION_SPACE_H
+
+#include <exceptions/exceptions.h>
+#include <boost/shared_ptr.hpp>
+#include <map>
+#include <stdint.h>
+#include <string>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Exception to be thrown when invalid option space
+/// is specified.
+class InvalidOptionSpace : public Exception {
+public:
+    InvalidOptionSpace(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) { };
+};
+
+/// OptionSpace forward declaration.
+class OptionSpace;
+/// A pointer to OptionSpace object.
+typedef boost::shared_ptr<OptionSpace> OptionSpacePtr;
+/// A collection of option spaces.
+typedef std::map<std::string, OptionSpacePtr> OptionSpaceCollection;
+
+/// @brief DHCP option space.
+///
+/// This class represents single option space. The option spaces are used
+/// to group DHCP options having unique option codes. The special type
+/// of the option space is so called "vendor specific option space".
+/// It groups sub-options being sent within Vendor Encapsulated Options.
+/// For DHCPv4 it is the option with code 43. The option spaces are
+/// assigned to option instances represented by isc::dhcp::Option and
+/// other classes derived from it. Each particular option may belong to
+/// multiple option spaces.
+/// This class may be used to represent any DHCPv4 option space. If the
+/// option space is to group DHCPv4 Vendor Encapsulated Options then
+/// "vendor space" flag must be set using \ref OptionSpace::setVendorSpace
+/// or the argument passed to the constructor. In theory, this class can
+/// be also used to represent non-vendor specific DHCPv6 option space
+/// but this is discouraged. For DHCPv6 option spaces the OptionSpace6
+/// class should be used instead.
+///
+/// @note this class is intended to be used to represent DHCPv4 option
+/// spaces only. However, it hasn't been called OptionSpace4 (that would
+/// suggest that it is specific to DHCPv4) because it can be also
+/// used to represent some DHCPv6 option spaces and is a base class
+/// for \ref OptionSpace6. Thus, if one declared the container as follows:
+/// @code
+/// std::vector<OptionSpace4> container;
+/// @endcode
+/// it would suggest that the container holds DHCPv4 option spaces while
+/// it could hold both DHCPv4 and DHCPv6 option spaces as the OptionSpace6
+/// object could be upcast to OptionSpace4. This confusion does not appear
+/// when OptionSpace is used as a name for the base class.
+class OptionSpace {
+public:
+
+    /// @brief Constructor.
+    ///
+    /// @param name option space name.
+    /// @param vendor_space boolean value that indicates that the object
+    /// describes the vendor specific option space.
+    ///
+    /// @throw isc::dhcp::InvalidOptionSpace if given option space name
+    /// contains invalid characters or is empty. This constructor uses
+    /// \ref validateName function to check that the specified name is
+    /// correct.
+    OptionSpace(const std::string& name, const bool vendor_space = false);
+
+    /// @brief Return option space name.
+    ///
+    /// @return option space name.
+    const std::string& getName() const { return (name_); }
+
+    /// @brief Mark option space as non-vendor space.
+    void clearVendorSpace() {
+        vendor_space_ = false;
+    }
+
+    /// @brief Check if option space is vendor specific.
+    ///
+    /// @return boolean value that indicates if the object describes
+    /// the vendor specific option space.
+    bool isVendorSpace() const { return (vendor_space_); }
+
+    /// @brief Mark option space as vendor specific.
+    void setVendorSpace() {
+        vendor_space_ = true;
+    }
+
+    /// @brief Checks that the provided option space name is valid.
+    ///
+    /// It is expected that option space name consists of upper or
+    /// lower case letters or digits. Also, it may contain underscores
+    /// or dashes. Other characters are prohibited. The empty option
+    /// space names are invalid.
+    ///
+    /// @param name option space name to be validated.
+    ///
+    /// @return true if the option space is valid, else it returns false.
+    static bool validateName(const std::string& name);
+
+private:
+    std::string name_;  ///< Holds option space name.
+
+    bool vendor_space_; ///< Is this the vendor space?
+
+};
+
+/// @brief DHCPv6 option space with enterprise number assigned.
+///
+/// This class extends the base class with the support for enterprise numbers.
+/// The enterprise numbers are assigned by IANA to various organizations
+/// and they are carried as uint32_t integers in DHCPv6 Vendor Specific
+/// Information Options (VSIO). For more information refer to RFC3315.
+/// All option spaces that group VSIO options must have enterprise number
+/// set. It can be set using a constructor or \ref setVendorSpace function.
+/// The extra functionality of this class (enterprise numbers) allows to
+/// represent DHCPv6 vendor-specific option spaces but this class is also
+/// intended to be used for all other DHCPv6 option spaces. That way all
+/// DHCPv6 option spaces can be stored in the container holding OptionSpace6
+/// objects. Also, it is easy to mark vendor-specific option space as non-vendor
+/// specific option space (and the other way around) without a need to cast
+/// between OptionSpace and OptionSpace6 types.
+class OptionSpace6 : public OptionSpace {
+public:
+
+    /// @brief Constructor for non-vendor-specific options.
+    ///
+    /// This constructor marks option space as non-vendor specific.
+    ///
+    /// @param name option space name.
+    ///
+    /// @throw isc::dhcp::InvalidOptionSpace if given option space name
+    /// contains invalid characters or is empty. This constructor uses
+    /// \ref OptionSpace::validateName function to check that the specified
+    /// name is correct.
+    OptionSpace6(const std::string& name);
+
+    /// @brief Constructor for vendor-specific options.
+    ///
+    /// This constructor marks option space as vendor specific and sets
+    /// enterprise number to a given value.
+    ///
+    /// @param name option space name.
+    /// @param enterprise_number enterprise number.
+    ///
+    /// @throw isc::dhcp::InvalidOptionSpace if given option space name
+    /// contains invalid characters or is empty. This constructor uses
+    /// \ref OptionSpace::validateName function to check that the specified
+    /// name is correct.
+    OptionSpace6(const std::string& name, const uint32_t enterprise_number);
+
+    /// @brief Return enterprise number for the option space.
+    ///
+    /// @return enterprise number.
+    uint32_t getEnterpriseNumber() const { return (enterprise_number_); }
+
+    /// @brief Mark option space as vendor specific.
+    ///
+    /// @param enterprise_number enterprise number.
+    void setVendorSpace(const uint32_t enterprise_number);
+
+private:
+    
+    uint32_t enterprise_number_; ///< IANA assigned enterprise number.
+};
+
+} // namespace isc::dhcp
+} // namespace isc
+
+#endif // OPTION_SPACE_H
diff --git a/src/lib/dhcp/std_option_defs.h b/src/lib/dhcp/std_option_defs.h
index 144df8b..839a5d9 100644
--- a/src/lib/dhcp/std_option_defs.h
+++ b/src/lib/dhcp/std_option_defs.h
@@ -50,6 +50,8 @@ struct OptionDefParams {
     bool array;                    // is array
     const OptionDataType* records; // record fields
     size_t records_size;           // number of fields in a record
+    const char* encapsulates;      // option space encapsulated by
+                                   // the particular option.
 };
 
 // fqdn option record fields.
@@ -64,128 +66,128 @@ RECORD_DECL(FQDN_RECORDS, OPT_UINT8_TYPE, OPT_UINT8_TYPE, OPT_STRING_TYPE);
 
 /// @brief Definitions of standard DHCPv4 options.
 const OptionDefParams OPTION_DEF_PARAMS4[] = {
-    { "subnet-mask", DHO_SUBNET_MASK, OPT_IPV4_ADDRESS_TYPE, false, NO_RECORD_DEF },
-    { "time-offset", DHO_TIME_OFFSET, OPT_UINT32_TYPE, false, NO_RECORD_DEF },
-    { "routers", DHO_ROUTERS, OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF },
-    { "time-servers", DHO_TIME_SERVERS, OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF },
+    { "subnet-mask", DHO_SUBNET_MASK, OPT_IPV4_ADDRESS_TYPE, false, NO_RECORD_DEF, "" },
+    { "time-offset", DHO_TIME_OFFSET, OPT_UINT32_TYPE, false, NO_RECORD_DEF, "" },
+    { "routers", DHO_ROUTERS, OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF, "" },
+    { "time-servers", DHO_TIME_SERVERS, OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF, "" },
     { "name-servers", DHO_NAME_SERVERS, OPT_IPV4_ADDRESS_TYPE,
-      false, NO_RECORD_DEF },
+      false, NO_RECORD_DEF, "" },
     { "domain-name-servers", DHO_DOMAIN_NAME_SERVERS,
-      OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF },
-    { "log-servers", DHO_LOG_SERVERS, OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF },
+      OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF, "" },
+    { "log-servers", DHO_LOG_SERVERS, OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF, "" },
     { "cookie-servers", DHO_COOKIE_SERVERS, OPT_IPV4_ADDRESS_TYPE,
-      true, NO_RECORD_DEF },
-    { "lpr-servers", DHO_LPR_SERVERS, OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF },
-    { "impress-servers", DHO_IMPRESS_SERVERS, OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF },
+      true, NO_RECORD_DEF, "" },
+    { "lpr-servers", DHO_LPR_SERVERS, OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF, "" },
+    { "impress-servers", DHO_IMPRESS_SERVERS, OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF, "" },
     { "resource-location-servers", DHO_RESOURCE_LOCATION_SERVERS,
-      OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF },
-    { "host-name", DHO_HOST_NAME, OPT_STRING_TYPE, false, NO_RECORD_DEF },
-    { "boot-size", DHO_BOOT_SIZE, OPT_UINT16_TYPE, false, NO_RECORD_DEF },
-    { "merit-dump", DHO_MERIT_DUMP, OPT_STRING_TYPE, false, NO_RECORD_DEF },
-    { "domain-name", DHO_DOMAIN_NAME, OPT_FQDN_TYPE, false, NO_RECORD_DEF },
-    { "swap-server", DHO_SWAP_SERVER, OPT_IPV4_ADDRESS_TYPE, false, NO_RECORD_DEF },
-    { "root-path", DHO_ROOT_PATH, OPT_STRING_TYPE, false, NO_RECORD_DEF },
+      OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF, "" },
+    { "host-name", DHO_HOST_NAME, OPT_STRING_TYPE, false, NO_RECORD_DEF, "" },
+    { "boot-size", DHO_BOOT_SIZE, OPT_UINT16_TYPE, false, NO_RECORD_DEF, "" },
+    { "merit-dump", DHO_MERIT_DUMP, OPT_STRING_TYPE, false, NO_RECORD_DEF, "" },
+    { "domain-name", DHO_DOMAIN_NAME, OPT_FQDN_TYPE, false, NO_RECORD_DEF, "" },
+    { "swap-server", DHO_SWAP_SERVER, OPT_IPV4_ADDRESS_TYPE, false, NO_RECORD_DEF, "" },
+    { "root-path", DHO_ROOT_PATH, OPT_STRING_TYPE, false, NO_RECORD_DEF, "" },
     { "extensions-path", DHO_EXTENSIONS_PATH, OPT_STRING_TYPE,
-      false, NO_RECORD_DEF },
-    { "ip-forwarding", DHO_IP_FORWARDING, OPT_BOOLEAN_TYPE, false, NO_RECORD_DEF },
+      false, NO_RECORD_DEF, "" },
+    { "ip-forwarding", DHO_IP_FORWARDING, OPT_BOOLEAN_TYPE, false, NO_RECORD_DEF, "" },
     { "non-local-source-routing", DHO_NON_LOCAL_SOURCE_ROUTING,
-      OPT_BOOLEAN_TYPE, false, NO_RECORD_DEF },
-    { "policy-filter", DHO_POLICY_FILTER, OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF },
+      OPT_BOOLEAN_TYPE, false, NO_RECORD_DEF, "" },
+    { "policy-filter", DHO_POLICY_FILTER, OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF, "" },
     { "max-dgram-reassembly", DHO_MAX_DGRAM_REASSEMBLY,
-      OPT_UINT16_TYPE, false, NO_RECORD_DEF },
-    { "default-ip-ttl", DHO_DEFAULT_IP_TTL, OPT_UINT8_TYPE, false, NO_RECORD_DEF },
+      OPT_UINT16_TYPE, false, NO_RECORD_DEF, "" },
+    { "default-ip-ttl", DHO_DEFAULT_IP_TTL, OPT_UINT8_TYPE, false, NO_RECORD_DEF, "" },
     { "path-mtu-aging-timeout", DHO_PATH_MTU_AGING_TIMEOUT,
-      OPT_UINT32_TYPE, false, NO_RECORD_DEF },
+      OPT_UINT32_TYPE, false, NO_RECORD_DEF, "" },
     { "path-mtu-plateau-table", DHO_PATH_MTU_PLATEAU_TABLE,
-      OPT_UINT16_TYPE, true, NO_RECORD_DEF },
-    { "interface-mtu", DHO_INTERFACE_MTU, OPT_UINT16_TYPE, false, NO_RECORD_DEF },
+      OPT_UINT16_TYPE, true, NO_RECORD_DEF, "" },
+    { "interface-mtu", DHO_INTERFACE_MTU, OPT_UINT16_TYPE, false, NO_RECORD_DEF, "" },
     { "all-subnets-local", DHO_ALL_SUBNETS_LOCAL,
-      OPT_BOOLEAN_TYPE, false, NO_RECORD_DEF },
+      OPT_BOOLEAN_TYPE, false, NO_RECORD_DEF, "" },
     { "broadcast-address", DHO_BROADCAST_ADDRESS,
-      OPT_IPV4_ADDRESS_TYPE, false, NO_RECORD_DEF },
+      OPT_IPV4_ADDRESS_TYPE, false, NO_RECORD_DEF, "" },
     { "perform-mask-discovery", DHO_PERFORM_MASK_DISCOVERY,
-      OPT_BOOLEAN_TYPE, false, NO_RECORD_DEF },
-    { "mask-supplier", DHO_MASK_SUPPLIER, OPT_BOOLEAN_TYPE, false, NO_RECORD_DEF },
+      OPT_BOOLEAN_TYPE, false, NO_RECORD_DEF, "" },
+    { "mask-supplier", DHO_MASK_SUPPLIER, OPT_BOOLEAN_TYPE, false, NO_RECORD_DEF, "" },
     { "router-discovery", DHO_ROUTER_DISCOVERY,
-      OPT_BOOLEAN_TYPE, false, NO_RECORD_DEF },
+      OPT_BOOLEAN_TYPE, false, NO_RECORD_DEF, "" },
     { "router-solicitation-address", DHO_ROUTER_SOLICITATION_ADDRESS,
-      OPT_IPV4_ADDRESS_TYPE, false, NO_RECORD_DEF },
+      OPT_IPV4_ADDRESS_TYPE, false, NO_RECORD_DEF, "" },
     { "static-routes", DHO_STATIC_ROUTES,
-      OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF },
+      OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF, "" },
     { "trailer-encapsulation", DHO_TRAILER_ENCAPSULATION,
-      OPT_BOOLEAN_TYPE, false, NO_RECORD_DEF },
+      OPT_BOOLEAN_TYPE, false, NO_RECORD_DEF, "" },
     { "arp-cache-timeout", DHO_ARP_CACHE_TIMEOUT,
-      OPT_UINT32_TYPE, false, NO_RECORD_DEF },
+      OPT_UINT32_TYPE, false, NO_RECORD_DEF, "" },
     { "ieee802-3-encapsulation", DHO_IEEE802_3_ENCAPSULATION,
-      OPT_BOOLEAN_TYPE, false, NO_RECORD_DEF },
-    { "default-tcp-ttl", DHO_DEFAULT_TCP_TTL, OPT_UINT8_TYPE, false, NO_RECORD_DEF },
+      OPT_BOOLEAN_TYPE, false, NO_RECORD_DEF, "" },
+    { "default-tcp-ttl", DHO_DEFAULT_TCP_TTL, OPT_UINT8_TYPE, false, NO_RECORD_DEF, "" },
     { "tcp-keepalive-internal", DHO_TCP_KEEPALIVE_INTERVAL,
-      OPT_UINT32_TYPE, false, NO_RECORD_DEF },
+      OPT_UINT32_TYPE, false, NO_RECORD_DEF, "" },
     { "tcp-keepalive-garbage", DHO_TCP_KEEPALIVE_GARBAGE,
-      OPT_BOOLEAN_TYPE, false, NO_RECORD_DEF },
-    { "nis-domain", DHO_NIS_DOMAIN, OPT_STRING_TYPE, false, NO_RECORD_DEF },
-    { "nis-servers", DHO_NIS_SERVERS, OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF },
-    { "ntp-servers", DHO_NTP_SERVERS, OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF },
+      OPT_BOOLEAN_TYPE, false, NO_RECORD_DEF, "" },
+    { "nis-domain", DHO_NIS_DOMAIN, OPT_STRING_TYPE, false, NO_RECORD_DEF, "" },
+    { "nis-servers", DHO_NIS_SERVERS, OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF, "" },
+    { "ntp-servers", DHO_NTP_SERVERS, OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF, "" },
     { "vendor-encapsulated-options", DHO_VENDOR_ENCAPSULATED_OPTIONS,
-      OPT_BINARY_TYPE, false, NO_RECORD_DEF },
+      OPT_EMPTY_TYPE, false, NO_RECORD_DEF, "vendor-encapsulated-options-space" },
     { "netbios-name-servers", DHO_NETBIOS_NAME_SERVERS,
-      OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF },
+      OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF, "" },
     { "netbios-dd-server", DHO_NETBIOS_DD_SERVER,
-      OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF },
+      OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF, "" },
     { "netbios-node-type", DHO_NETBIOS_NODE_TYPE,
-      OPT_UINT8_TYPE, false, NO_RECORD_DEF },
-    { "netbios-scope", DHO_NETBIOS_SCOPE, OPT_STRING_TYPE, false, NO_RECORD_DEF },
-    { "font-servers", DHO_FONT_SERVERS, OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF },
+      OPT_UINT8_TYPE, false, NO_RECORD_DEF, "" },
+    { "netbios-scope", DHO_NETBIOS_SCOPE, OPT_STRING_TYPE, false, NO_RECORD_DEF, "" },
+    { "font-servers", DHO_FONT_SERVERS, OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF, "" },
     { "x-display-manager", DHO_X_DISPLAY_MANAGER,
-      OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF },
+      OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF, "" },
     { "dhcp-requested-address", DHO_DHCP_REQUESTED_ADDRESS,
-      OPT_IPV4_ADDRESS_TYPE, false, NO_RECORD_DEF },
-    { "dhcp-lease-time", DHO_DHCP_LEASE_TIME, OPT_UINT32_TYPE, false, NO_RECORD_DEF },
+      OPT_IPV4_ADDRESS_TYPE, false, NO_RECORD_DEF, "" },
+    { "dhcp-lease-time", DHO_DHCP_LEASE_TIME, OPT_UINT32_TYPE, false, NO_RECORD_DEF, "" },
     { "dhcp-option-overload", DHO_DHCP_OPTION_OVERLOAD,
-      OPT_UINT8_TYPE, false, NO_RECORD_DEF },
-    { "dhcp-message-type", DHO_DHCP_MESSAGE_TYPE, OPT_UINT8_TYPE, false, NO_RECORD_DEF },
+      OPT_UINT8_TYPE, false, NO_RECORD_DEF, "" },
+    { "dhcp-message-type", DHO_DHCP_MESSAGE_TYPE, OPT_UINT8_TYPE, false, NO_RECORD_DEF, "" },
     { "dhcp-server-identifier", DHO_DHCP_SERVER_IDENTIFIER,
-      OPT_IPV4_ADDRESS_TYPE, false, NO_RECORD_DEF },
+      OPT_IPV4_ADDRESS_TYPE, false, NO_RECORD_DEF, "" },
     { "dhcp-parameter-request-list", DHO_DHCP_PARAMETER_REQUEST_LIST,
-      OPT_UINT8_TYPE, true, NO_RECORD_DEF },
-    { "dhcp-message", DHO_DHCP_MESSAGE, OPT_STRING_TYPE, false, NO_RECORD_DEF },
+      OPT_UINT8_TYPE, true, NO_RECORD_DEF, "" },
+    { "dhcp-message", DHO_DHCP_MESSAGE, OPT_STRING_TYPE, false, NO_RECORD_DEF, "" },
     { "dhcp-max-message-size", DHO_DHCP_MAX_MESSAGE_SIZE,
-      OPT_UINT16_TYPE, false, NO_RECORD_DEF },
-    { "dhcp-renewal-time", DHO_DHCP_RENEWAL_TIME, OPT_UINT32_TYPE, false, NO_RECORD_DEF },
+      OPT_UINT16_TYPE, false, NO_RECORD_DEF, "" },
+    { "dhcp-renewal-time", DHO_DHCP_RENEWAL_TIME, OPT_UINT32_TYPE, false, NO_RECORD_DEF, "" },
     { "dhcp-rebinding-time", DHO_DHCP_REBINDING_TIME,
-      OPT_UINT32_TYPE, false, NO_RECORD_DEF },
+      OPT_UINT32_TYPE, false, NO_RECORD_DEF, "" },
     { "vendor-class-identifier", DHO_VENDOR_CLASS_IDENTIFIER,
-      OPT_BINARY_TYPE, false, NO_RECORD_DEF },
+      OPT_BINARY_TYPE, false, NO_RECORD_DEF, "" },
     { "dhcp-client-identifier", DHO_DHCP_CLIENT_IDENTIFIER,
-      OPT_BINARY_TYPE, false, NO_RECORD_DEF },
-    { "nwip-domain-name", DHO_NWIP_DOMAIN_NAME, OPT_STRING_TYPE, false, NO_RECORD_DEF },
-    { "nwip-suboptions", DHO_NWIP_SUBOPTIONS, OPT_BINARY_TYPE, false, NO_RECORD_DEF },
-    { "user-class", DHO_USER_CLASS, OPT_BINARY_TYPE, false, NO_RECORD_DEF },
-    { "fqdn", DHO_FQDN, OPT_RECORD_TYPE, false, RECORD_DEF(FQDN_RECORDS) },
+      OPT_BINARY_TYPE, false, NO_RECORD_DEF, "" },
+    { "nwip-domain-name", DHO_NWIP_DOMAIN_NAME, OPT_STRING_TYPE, false, NO_RECORD_DEF, "" },
+    { "nwip-suboptions", DHO_NWIP_SUBOPTIONS, OPT_BINARY_TYPE, false, NO_RECORD_DEF, "" },
+    { "user-class", DHO_USER_CLASS, OPT_BINARY_TYPE, false, NO_RECORD_DEF, "" },
+    { "fqdn", DHO_FQDN, OPT_RECORD_TYPE, false, RECORD_DEF(FQDN_RECORDS), "" },
     { "dhcp-agent-options", DHO_DHCP_AGENT_OPTIONS,
-      OPT_BINARY_TYPE, false, NO_RECORD_DEF },
+      OPT_EMPTY_TYPE, false, NO_RECORD_DEF, "dhcp-agent-options-space" },
     // Unfortunatelly the AUTHENTICATE option contains a 64-bit
     // data field called 'replay-detection' that can't be added
     // as a record field to a custom option. Also, there is no
     // dedicated option class to handle it so we simply return
     // binary option type for now.
     // @todo implement a class to handle AUTH option.
-    { "authenticate", DHO_AUTHENTICATE, OPT_BINARY_TYPE, false, NO_RECORD_DEF },
+    { "authenticate", DHO_AUTHENTICATE, OPT_BINARY_TYPE, false, NO_RECORD_DEF, "" },
     { "client-last-transaction-time", DHO_CLIENT_LAST_TRANSACTION_TIME,
-      OPT_UINT32_TYPE, false, NO_RECORD_DEF },
-    { "associated-ip", DHO_ASSOCIATED_IP, OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF },
+      OPT_UINT32_TYPE, false, NO_RECORD_DEF, "" },
+    { "associated-ip", DHO_ASSOCIATED_IP, OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF, "" },
     { "subnet-selection", DHO_SUBNET_SELECTION,
-      OPT_IPV4_ADDRESS_TYPE, false, NO_RECORD_DEF },
+      OPT_IPV4_ADDRESS_TYPE, false, NO_RECORD_DEF, "" },
     // The following options need a special encoding of data
     // being carried by them. Therefore, there is no way they can
     // be handled by OptionCustom. We may need to implement
     // dedicated classes to handle them. Until that happens
     // let's treat them as 'binary' options.
-    { "domain-search", DHO_DOMAIN_SEARCH, OPT_BINARY_TYPE, false, NO_RECORD_DEF },
+    { "domain-search", DHO_DOMAIN_SEARCH, OPT_BINARY_TYPE, false, NO_RECORD_DEF, "" },
     { "vivco-suboptions", DHO_VIVCO_SUBOPTIONS,
-      OPT_BINARY_TYPE, false, NO_RECORD_DEF },
+      OPT_BINARY_TYPE, false, NO_RECORD_DEF, "" },
     { "vivso-suboptions", DHO_VIVSO_SUBOPTIONS, OPT_BINARY_TYPE,
-      false, NO_RECORD_DEF }
+      false, NO_RECORD_DEF, "" }
 
         // @todo add definitions for all remaning options.
 };
@@ -222,8 +224,6 @@ RECORD_DECL(REMOTE_ID_RECORDS, OPT_UINT32_TYPE, OPT_BINARY_TYPE);
 RECORD_DECL(STATUS_CODE_RECORDS, OPT_UINT16_TYPE, OPT_STRING_TYPE);
 // vendor-class
 RECORD_DECL(VENDOR_CLASS_RECORDS, OPT_UINT32_TYPE, OPT_BINARY_TYPE);
-// vendor-opts
-RECORD_DECL(VENDOR_OPTS_RECORDS, OPT_UINT32_TYPE, OPT_BINARY_TYPE);
 
 /// Standard DHCPv6 option definitions.
 ///
@@ -236,84 +236,84 @@ RECORD_DECL(VENDOR_OPTS_RECORDS, OPT_UINT32_TYPE, OPT_BINARY_TYPE);
 /// warning about lack of initializers for some struct members
 /// causing build to fail.
 const OptionDefParams OPTION_DEF_PARAMS6[] = {
-    { "clientid", D6O_CLIENTID, OPT_BINARY_TYPE, false, NO_RECORD_DEF },
-    { "serverid", D6O_SERVERID, OPT_BINARY_TYPE, false, NO_RECORD_DEF },
-    { "ia-na", D6O_IA_NA, OPT_RECORD_TYPE, false, RECORD_DEF(IA_NA_RECORDS) },
-    { "ia-ta", D6O_IA_TA, OPT_UINT32_TYPE, false, NO_RECORD_DEF },
-    { "iaaddr", D6O_IAADDR, OPT_RECORD_TYPE, false, RECORD_DEF(IAADDR_RECORDS) },
-    { "oro", D6O_ORO, OPT_UINT16_TYPE, true, NO_RECORD_DEF },
-    { "preference", D6O_PREFERENCE, OPT_UINT8_TYPE, false, NO_RECORD_DEF },
-    { "elapsed-time", D6O_ELAPSED_TIME, OPT_UINT16_TYPE, false, NO_RECORD_DEF },
-    { "relay-msg", D6O_RELAY_MSG, OPT_BINARY_TYPE, false, NO_RECORD_DEF },
+    { "clientid", D6O_CLIENTID, OPT_BINARY_TYPE, false, NO_RECORD_DEF, "" },
+    { "serverid", D6O_SERVERID, OPT_BINARY_TYPE, false, NO_RECORD_DEF, "" },
+    { "ia-na", D6O_IA_NA, OPT_RECORD_TYPE, false, RECORD_DEF(IA_NA_RECORDS), "" },
+    { "ia-ta", D6O_IA_TA, OPT_UINT32_TYPE, false, NO_RECORD_DEF, "" },
+    { "iaaddr", D6O_IAADDR, OPT_RECORD_TYPE, false, RECORD_DEF(IAADDR_RECORDS), "" },
+    { "oro", D6O_ORO, OPT_UINT16_TYPE, true, NO_RECORD_DEF, "" },
+    { "preference", D6O_PREFERENCE, OPT_UINT8_TYPE, false, NO_RECORD_DEF, "" },
+    { "elapsed-time", D6O_ELAPSED_TIME, OPT_UINT16_TYPE, false, NO_RECORD_DEF, "" },
+    { "relay-msg", D6O_RELAY_MSG, OPT_BINARY_TYPE, false, NO_RECORD_DEF, "" },
     // Unfortunatelly the AUTH option contains a 64-bit data field
     // called 'replay-detection' that can't be added as a record
     // field to a custom option. Also, there is no dedicated
     // option class to handle it so we simply return binary
     // option type for now.
     // @todo implement a class to handle AUTH option.
-    { "auth", D6O_AUTH, OPT_BINARY_TYPE, false, NO_RECORD_DEF },
-    { "unicast", D6O_UNICAST, OPT_IPV6_ADDRESS_TYPE, false, NO_RECORD_DEF },
+    { "auth", D6O_AUTH, OPT_BINARY_TYPE, false, NO_RECORD_DEF, "" },
+    { "unicast", D6O_UNICAST, OPT_IPV6_ADDRESS_TYPE, false, NO_RECORD_DEF, "" },
     { "status-code", D6O_STATUS_CODE, OPT_RECORD_TYPE, false,
-      RECORD_DEF(STATUS_CODE_RECORDS) },
-    { "rapid-commit", D6O_RAPID_COMMIT, OPT_EMPTY_TYPE, false, NO_RECORD_DEF },
-    { "user-class", D6O_USER_CLASS, OPT_BINARY_TYPE, false, NO_RECORD_DEF },
+      RECORD_DEF(STATUS_CODE_RECORDS), "" },
+    { "rapid-commit", D6O_RAPID_COMMIT, OPT_EMPTY_TYPE, false, NO_RECORD_DEF, "" },
+    { "user-class", D6O_USER_CLASS, OPT_BINARY_TYPE, false, NO_RECORD_DEF, "" },
     { "vendor-class", D6O_VENDOR_CLASS, OPT_RECORD_TYPE, false,
-      RECORD_DEF(VENDOR_CLASS_RECORDS) },
-    { "vendor-opts", D6O_VENDOR_OPTS, OPT_RECORD_TYPE, false,
-      RECORD_DEF(VENDOR_OPTS_RECORDS) },
-    { "interface-id", D6O_INTERFACE_ID, OPT_BINARY_TYPE, false, NO_RECORD_DEF },
-    { "reconf-msg", D6O_RECONF_MSG, OPT_UINT8_TYPE, false, NO_RECORD_DEF },
+      RECORD_DEF(VENDOR_CLASS_RECORDS), "" },
+    { "vendor-opts", D6O_VENDOR_OPTS, OPT_UINT32_TYPE, false,
+      NO_RECORD_DEF, "vendor-opts-space" },
+    { "interface-id", D6O_INTERFACE_ID, OPT_BINARY_TYPE, false, NO_RECORD_DEF, "" },
+    { "reconf-msg", D6O_RECONF_MSG, OPT_UINT8_TYPE, false, NO_RECORD_DEF, "" },
     { "reconf-accept", D6O_RECONF_ACCEPT, OPT_EMPTY_TYPE, false,
-      NO_RECORD_DEF },
+      NO_RECORD_DEF, "" },
     { "sip-server-dns", D6O_SIP_SERVERS_DNS, OPT_FQDN_TYPE, true,
-      NO_RECORD_DEF },
+      NO_RECORD_DEF, "" },
     { "sip-server-addr", D6O_SIP_SERVERS_ADDR, OPT_IPV6_ADDRESS_TYPE, true,
-      NO_RECORD_DEF },
+      NO_RECORD_DEF, "" },
     { "dns-servers", D6O_NAME_SERVERS, OPT_IPV6_ADDRESS_TYPE, true,
-      NO_RECORD_DEF },
-    { "domain-search", D6O_DOMAIN_SEARCH, OPT_FQDN_TYPE, true, NO_RECORD_DEF },
-    { "ia-pd", D6O_IA_PD, OPT_RECORD_TYPE, false, RECORD_DEF(IA_PD_RECORDS) },
+      NO_RECORD_DEF, "" },
+    { "domain-search", D6O_DOMAIN_SEARCH, OPT_FQDN_TYPE, true, NO_RECORD_DEF, "" },
+    { "ia-pd", D6O_IA_PD, OPT_RECORD_TYPE, false, RECORD_DEF(IA_PD_RECORDS), "" },
     { "iaprefix", D6O_IAPREFIX, OPT_RECORD_TYPE, false,
-      RECORD_DEF(IA_PREFIX_RECORDS) },
+      RECORD_DEF(IA_PREFIX_RECORDS), "" },
     { "nis-servers", D6O_NIS_SERVERS, OPT_IPV6_ADDRESS_TYPE, true,
-      NO_RECORD_DEF },
+      NO_RECORD_DEF, "" },
     { "nisp-servers", D6O_NISP_SERVERS, OPT_IPV6_ADDRESS_TYPE, true,
-      NO_RECORD_DEF },
+      NO_RECORD_DEF, "" },
     { "nis-domain-name", D6O_NIS_DOMAIN_NAME, OPT_FQDN_TYPE, true,
-      NO_RECORD_DEF },
+      NO_RECORD_DEF, "" },
     { "nisp-domain-name", D6O_NISP_DOMAIN_NAME, OPT_FQDN_TYPE, true,
-      NO_RECORD_DEF },
+      NO_RECORD_DEF, "" },
     { "sntp-servers", D6O_SNTP_SERVERS, OPT_IPV6_ADDRESS_TYPE, true,
-      NO_RECORD_DEF },
+      NO_RECORD_DEF, "" },
     { "information-refresh-time", D6O_INFORMATION_REFRESH_TIME,
-      OPT_UINT32_TYPE, false, NO_RECORD_DEF },
+      OPT_UINT32_TYPE, false, NO_RECORD_DEF, "" },
     { "bcmcs-server-dns", D6O_BCMCS_SERVER_D, OPT_FQDN_TYPE, true,
-      NO_RECORD_DEF },
+      NO_RECORD_DEF, "" },
     { "bcmcs-server-addr", D6O_BCMCS_SERVER_A, OPT_IPV6_ADDRESS_TYPE, true,
-      NO_RECORD_DEF },
+      NO_RECORD_DEF, "" },
     { "geoconf-civic", D6O_GEOCONF_CIVIC, OPT_RECORD_TYPE, false,
-      RECORD_DEF(GEOCONF_CIVIC_RECORDS) },
+      RECORD_DEF(GEOCONF_CIVIC_RECORDS), "" },
     { "remote-id", D6O_REMOTE_ID, OPT_RECORD_TYPE, false,
-      RECORD_DEF(REMOTE_ID_RECORDS) },
+      RECORD_DEF(REMOTE_ID_RECORDS), "" },
     { "subscriber-id", D6O_SUBSCRIBER_ID, OPT_BINARY_TYPE, false,
-      NO_RECORD_DEF },
+      NO_RECORD_DEF, "" },
     { "client-fqdn", D6O_CLIENT_FQDN, OPT_RECORD_TYPE, false,
-      RECORD_DEF(CLIENT_FQDN_RECORDS) },
+      RECORD_DEF(CLIENT_FQDN_RECORDS), "" },
     { "pana-agent", D6O_PANA_AGENT, OPT_IPV6_ADDRESS_TYPE, true,
-      NO_RECORD_DEF },
+      NO_RECORD_DEF, "" },
     { "new-posix-timezone", D6O_NEW_POSIX_TIMEZONE, OPT_STRING_TYPE, false,
-      NO_RECORD_DEF },
+      NO_RECORD_DEF, "" },
     { "new-tzdb-timezone", D6O_NEW_TZDB_TIMEZONE, OPT_STRING_TYPE, false,
-      NO_RECORD_DEF },
-    { "ero", D6O_ERO, OPT_UINT16_TYPE, true, NO_RECORD_DEF },
+      NO_RECORD_DEF, "" },
+    { "ero", D6O_ERO, OPT_UINT16_TYPE, true, NO_RECORD_DEF, "" },
     { "lq-query", D6O_LQ_QUERY, OPT_RECORD_TYPE, false,
-      RECORD_DEF(LQ_QUERY_RECORDS) },
-    { "client-data", D6O_CLIENT_DATA, OPT_EMPTY_TYPE, false, NO_RECORD_DEF },
-    { "clt-time", D6O_CLT_TIME, OPT_UINT32_TYPE, false, NO_RECORD_DEF },
+      RECORD_DEF(LQ_QUERY_RECORDS), "" },
+    { "client-data", D6O_CLIENT_DATA, OPT_EMPTY_TYPE, false, NO_RECORD_DEF, "" },
+    { "clt-time", D6O_CLT_TIME, OPT_UINT32_TYPE, false, NO_RECORD_DEF, "" },
     { "lq-relay-data", D6O_LQ_RELAY_DATA, OPT_RECORD_TYPE, false,
-      RECORD_DEF(LQ_RELAY_DATA_RECORDS) },
+      RECORD_DEF(LQ_RELAY_DATA_RECORDS), "" },
     { "lq-client-link", D6O_LQ_CLIENT_LINK, OPT_IPV6_ADDRESS_TYPE, true,
-      NO_RECORD_DEF }
+      NO_RECORD_DEF, "" }
 
     // @todo There is still a bunch of options for which we have to provide
     // definitions but we don't do it because they are not really
diff --git a/src/lib/dhcp/tests/Makefile.am b/src/lib/dhcp/tests/Makefile.am
index 4833fb5..c868553 100644
--- a/src/lib/dhcp/tests/Makefile.am
+++ b/src/lib/dhcp/tests/Makefile.am
@@ -40,6 +40,7 @@ libdhcp___unittests_SOURCES += option_data_types_unittest.cc
 libdhcp___unittests_SOURCES += option_definition_unittest.cc
 libdhcp___unittests_SOURCES += option_custom_unittest.cc
 libdhcp___unittests_SOURCES += option_unittest.cc
+libdhcp___unittests_SOURCES += option_space_unittest.cc
 libdhcp___unittests_SOURCES += pkt4_unittest.cc
 libdhcp___unittests_SOURCES += pkt6_unittest.cc
 libdhcp___unittests_SOURCES += duid_unittest.cc
diff --git a/src/lib/dhcp/tests/libdhcp++_unittest.cc b/src/lib/dhcp/tests/libdhcp++_unittest.cc
index a59da12..44b5cbe 100644
--- a/src/lib/dhcp/tests/libdhcp++_unittest.cc
+++ b/src/lib/dhcp/tests/libdhcp++_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2011-2012 Internet Systems Consortium, Inc. ("ISC")
+// 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
@@ -68,12 +68,16 @@ public:
     /// used to create option instance.
     /// @param expected_type type of the option created by the
     /// factory function returned by the option definition.
+    /// @param encapsulates name of the option space being encapsulated
+    /// by the option.
     static void testStdOptionDefs4(const uint16_t code,
                                    const OptionBufferConstIter begin,
                                    const OptionBufferConstIter end,
-                                   const std::type_info& expected_type) {
+                                   const std::type_info& expected_type,
+                                   const std::string& encapsulates = "") {
         // Use V4 universe.
-        testStdOptionDefs(Option::V4, code, begin, end, expected_type);
+        testStdOptionDefs(Option::V4, code, begin, end, expected_type,
+                          encapsulates);
     }
 
     /// @brief Test DHCPv6 option definition.
@@ -88,12 +92,16 @@ public:
     /// used to create option instance.
     /// @param expected_type type of the option created by the
     /// factory function returned by the option definition.
+    /// @param encapsulates name of the option space being encapsulated
+    /// by the option.
     static void testStdOptionDefs6(const uint16_t code,
                                    const OptionBufferConstIter begin,
                                    const OptionBufferConstIter end,
-                                   const std::type_info& expected_type) {
+                                   const std::type_info& expected_type,
+                                   const std::string& encapsulates = "") {
         // Use V6 universe.
-        testStdOptionDefs(Option::V6, code, begin, end, expected_type);
+        testStdOptionDefs(Option::V6, code, begin, end, expected_type,
+                          encapsulates);
     }
 private:
 
@@ -109,11 +117,14 @@ private:
     /// used to create option instance.
     /// @param expected_type type of the option created by the
     /// factory function returned by the option definition.
+    /// @param encapsulates name of the option space being encapsulated
+    /// by the option.
     static void testStdOptionDefs(const Option::Universe u,
                                   const uint16_t code,
                                   const OptionBufferConstIter begin,
                                   const OptionBufferConstIter end,
-                                  const std::type_info& expected_type) {
+                                  const std::type_info& expected_type,
+                                  const std::string& encapsulates) {
         // Get all option definitions, we will use them to extract
         // the definition for a particular option code.
         // We don't have to initialize option definitions here because they
@@ -141,6 +152,9 @@ private:
         ASSERT_NO_THROW(def->validate())
             << "Option definition for the option code " << code
             << " is invalid";
+        // Check that the valid encapsulated option space name
+        // has been specified.
+        EXPECT_EQ(encapsulates, def->getEncapsulatedSpace());
         OptionPtr option;
         // Create the option.
         ASSERT_NO_THROW(option = def->optionFactory(u, code, begin, end))
@@ -648,7 +662,8 @@ TEST_F(LibDhcpTest, stdOptionDefs4) {
                                     typeid(Option4AddrLst));
 
     LibDhcpTest::testStdOptionDefs4(DHO_VENDOR_ENCAPSULATED_OPTIONS, begin, end,
-                                    typeid(Option));
+                                    typeid(Option),
+                                    "vendor-encapsulated-options-space");
 
     LibDhcpTest::testStdOptionDefs4(DHO_NETBIOS_NAME_SERVERS, begin, end,
                                     typeid(Option4AddrLst));
@@ -717,7 +732,7 @@ TEST_F(LibDhcpTest, stdOptionDefs4) {
                                     typeid(OptionCustom));
 
     LibDhcpTest::testStdOptionDefs4(DHO_DHCP_AGENT_OPTIONS, begin, end,
-                                    typeid(Option));
+                                    typeid(Option), "dhcp-agent-options-space");
 
     LibDhcpTest::testStdOptionDefs4(DHO_AUTHENTICATE, begin, end,
                                     typeid(Option));
@@ -816,7 +831,8 @@ TEST_F(LibDhcpTest, stdOptionDefs6) {
                                     typeid(OptionCustom));
 
     LibDhcpTest::testStdOptionDefs6(D6O_VENDOR_OPTS, begin, end,
-                                    typeid(OptionCustom));
+                                    typeid(OptionInt<uint32_t>),
+                                    "vendor-opts-space");
 
     LibDhcpTest::testStdOptionDefs6(D6O_INTERFACE_ID, begin, end,
                                     typeid(Option));
diff --git a/src/lib/dhcp/tests/option_definition_unittest.cc b/src/lib/dhcp/tests/option_definition_unittest.cc
index 310c7bf..174bafb 100644
--- a/src/lib/dhcp/tests/option_definition_unittest.cc
+++ b/src/lib/dhcp/tests/option_definition_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-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
@@ -53,38 +53,62 @@ public:
 TEST_F(OptionDefinitionTest, constructor) {
     // Specify the option data type as string. This should get converted
     // to enum value returned by getType().
-    OptionDefinition opt_def1("OPTION_CLIENTID", 1, "string");
+    OptionDefinition opt_def1("OPTION_CLIENTID", D6O_CLIENTID, "string");
     EXPECT_EQ("OPTION_CLIENTID", opt_def1.getName());
 
     EXPECT_EQ(1, opt_def1.getCode());
     EXPECT_EQ(OPT_STRING_TYPE,  opt_def1.getType());
     EXPECT_FALSE(opt_def1.getArrayType());
+    EXPECT_TRUE(opt_def1.getEncapsulatedSpace().empty());
     EXPECT_NO_THROW(opt_def1.validate());
 
     // Specify the option data type as an enum value.
-    OptionDefinition opt_def2("OPTION_RAPID_COMMIT", 14,
+    OptionDefinition opt_def2("OPTION_RAPID_COMMIT", D6O_RAPID_COMMIT,
                               OPT_EMPTY_TYPE);
     EXPECT_EQ("OPTION_RAPID_COMMIT", opt_def2.getName());
     EXPECT_EQ(14, opt_def2.getCode());
     EXPECT_EQ(OPT_EMPTY_TYPE, opt_def2.getType());
     EXPECT_FALSE(opt_def2.getArrayType());
-    EXPECT_NO_THROW(opt_def1.validate());
+    EXPECT_TRUE(opt_def2.getEncapsulatedSpace().empty());
+    EXPECT_NO_THROW(opt_def2.validate());
+
+    // Specify encapsulated option space name and option data type
+    // as enum value.
+    OptionDefinition opt_def3("OPTION_VENDOR_OPTS", D6O_VENDOR_OPTS,
+                              OPT_UINT32_TYPE, "isc");
+    EXPECT_EQ("OPTION_VENDOR_OPTS", opt_def3.getName());
+    EXPECT_EQ(D6O_VENDOR_OPTS, opt_def3.getCode());
+    EXPECT_EQ(OPT_UINT32_TYPE, opt_def3.getType());
+    EXPECT_FALSE(opt_def3.getArrayType());
+    EXPECT_EQ("isc", opt_def3.getEncapsulatedSpace());
+    EXPECT_NO_THROW(opt_def3.validate());
+
+    // Specify encapsulated option space name and option data type
+    // as string value.
+    OptionDefinition opt_def4("OPTION_VENDOR_OPTS", D6O_VENDOR_OPTS,
+                              "uint32", "isc");
+    EXPECT_EQ("OPTION_VENDOR_OPTS", opt_def4.getName());
+    EXPECT_EQ(D6O_VENDOR_OPTS, opt_def4.getCode());
+    EXPECT_EQ(OPT_UINT32_TYPE, opt_def4.getType());
+    EXPECT_FALSE(opt_def4.getArrayType());
+    EXPECT_EQ("isc", opt_def4.getEncapsulatedSpace());
+    EXPECT_NO_THROW(opt_def4.validate());
 
     // Check if it is possible to set that option is an array.
-    OptionDefinition opt_def3("OPTION_NIS_SERVERS", 27,
+    OptionDefinition opt_def5("OPTION_NIS_SERVERS", 27,
                               OPT_IPV6_ADDRESS_TYPE,
                               true);
-    EXPECT_EQ("OPTION_NIS_SERVERS", opt_def3.getName());
-    EXPECT_EQ(27, opt_def3.getCode());
-    EXPECT_EQ(OPT_IPV6_ADDRESS_TYPE, opt_def3.getType());
-    EXPECT_TRUE(opt_def3.getArrayType());
-    EXPECT_NO_THROW(opt_def3.validate());
+    EXPECT_EQ("OPTION_NIS_SERVERS", opt_def5.getName());
+    EXPECT_EQ(27, opt_def5.getCode());
+    EXPECT_EQ(OPT_IPV6_ADDRESS_TYPE, opt_def5.getType());
+    EXPECT_TRUE(opt_def5.getArrayType());
+    EXPECT_NO_THROW(opt_def5.validate());
 
     // The created object is invalid if invalid data type is specified but
     // constructor shouldn't throw exception. The object is validated after
     // it has been created.
     EXPECT_NO_THROW(
-        OptionDefinition opt_def4("OPTION_SERVERID",
+        OptionDefinition opt_def6("OPTION_SERVERID",
                                   OPT_UNKNOWN_TYPE + 10,
                                   OPT_STRING_TYPE);
     );
@@ -213,6 +237,11 @@ TEST_F(OptionDefinitionTest, validate) {
                                "record");
     opt_def16.addRecordField("uint8");
     opt_def16.addRecordField("string");
+
+    // Check invalid encapsulated option space name.
+    OptionDefinition opt_def17("OPTION_VENDOR_OPTS", D6O_VENDOR_OPTS,
+                               "uint32", "invalid%space%name");
+    EXPECT_THROW(opt_def17.validate(), MalformedOptionDefinition);
 }
 
 
diff --git a/src/lib/dhcp/tests/option_space_unittest.cc b/src/lib/dhcp/tests/option_space_unittest.cc
new file mode 100644
index 0000000..d3a6f53
--- /dev/null
+++ b/src/lib/dhcp/tests/option_space_unittest.cc
@@ -0,0 +1,150 @@
+// Copyright (C) 2012, 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/option_space.h>
+
+#include <gtest/gtest.h>
+
+using namespace isc::dhcp;
+using namespace isc;
+
+namespace {
+
+// The purpose of this test is to verify that the constructor
+// creates an object with members initialized to correct values.
+TEST(OptionSpaceTest, constructor) {
+    // Create some option space.
+    OptionSpace space("isc", true);
+    EXPECT_EQ("isc", space.getName());
+    EXPECT_TRUE(space.isVendorSpace());
+
+    // Create another object with different values
+    // to check that the values will change.
+    OptionSpace space2("abc", false);
+    EXPECT_EQ("abc", space2.getName());
+    EXPECT_FALSE(space2.isVendorSpace());
+
+    // Verify that constructor throws exception if invalid
+    // option space name is provided.
+    EXPECT_THROW(OptionSpace("invalid%space.name"), InvalidOptionSpace);
+}
+
+// The purpose of this test is to verify that the vendor-space flag
+// can be overriden.
+TEST(OptionSpaceTest, setVendorSpace) {
+    OptionSpace space("isc", true);
+    EXPECT_EQ("isc", space.getName());
+    EXPECT_TRUE(space.isVendorSpace());
+
+    // Override the vendor space flag.
+    space.clearVendorSpace();
+    EXPECT_FALSE(space.isVendorSpace());
+}
+
+// The purpose of this test is to verify that the static function
+// to validate the option space name works correctly.
+TEST(OptionSpaceTest, validateName) {
+    // Positive test scenarios: letters, digits, dashes, underscores
+    // lower/upper case allowed.
+    EXPECT_TRUE(OptionSpace::validateName("abc"));
+    EXPECT_TRUE(OptionSpace::validateName("dash-allowed"));
+    EXPECT_TRUE(OptionSpace::validateName("two-dashes-allowed"));
+    EXPECT_TRUE(OptionSpace::validateName("underscore_allowed"));
+    EXPECT_TRUE(OptionSpace::validateName("underscore_three_times_allowed"));
+    EXPECT_TRUE(OptionSpace::validateName("digits0912"));
+    EXPECT_TRUE(OptionSpace::validateName("1234"));
+    EXPECT_TRUE(OptionSpace::validateName("UPPER_CASE_allowed"));
+
+    // Negative test scenarions: empty strings, dots, spaces are not
+    // allowed
+    EXPECT_FALSE(OptionSpace::validateName(""));
+    EXPECT_FALSE(OptionSpace::validateName(" "));
+    EXPECT_FALSE(OptionSpace::validateName(" isc "));
+    EXPECT_FALSE(OptionSpace::validateName("isc "));
+    EXPECT_FALSE(OptionSpace::validateName(" isc"));
+    EXPECT_FALSE(OptionSpace::validateName("isc with-space"));
+
+    // Hyphens and underscores are not allowed at the beginning
+    // and at the end of the option space name.
+    EXPECT_FALSE(OptionSpace::validateName("-isc"));
+    EXPECT_FALSE(OptionSpace::validateName("isc-"));
+    EXPECT_FALSE(OptionSpace::validateName("_isc"));
+    EXPECT_FALSE(OptionSpace::validateName("isc_"));
+
+    // Test other special characters
+    const char specials[] = { '!', '@', '#', '$', '%', '^', '&', '*', '(', ')',
+                              '+', '=', '[', ']', '{', '}', ';', ':', '"', '\'',
+                              '\\', '|', '<','>', ',', '.', '?', '~', '`' };
+    for (int i = 0; i < sizeof(specials); ++i) {
+        std::ostringstream stream;
+        // Concatenate valid option space name: "abc" with an invalid character.
+        // That way we get option space names like: "abc!", "abc$" etc. It is
+        // expected that the validating function fails form them.
+        stream << "abc" << specials[i];
+        EXPECT_FALSE(OptionSpace::validateName(stream.str()))
+            << "Test failed for special character '" << specials[i] << "'.";
+    }
+}
+
+// The purpose of this test is to verify that the constructors of the
+// OptionSpace6 class set the class members to correct values.
+TEST(OptionSpace6Test, constructor) {
+    // Create some option space and do not specify enterprise number.
+    // In such case the vendor space flag is expected to be
+    // set to false.
+    OptionSpace6 space1("abcd");
+    EXPECT_EQ("abcd", space1.getName());
+    EXPECT_FALSE(space1.isVendorSpace());
+
+    // Create an option space and specify an enterprise number. In this
+    // case the vendor space flag is expected to be set to true and the
+    // enterprise number should be set to a desired value.
+    OptionSpace6 space2("abcd", 2145);
+    EXPECT_EQ("abcd", space2.getName());
+    EXPECT_TRUE(space2.isVendorSpace());
+    EXPECT_EQ(2145, space2.getEnterpriseNumber());
+
+    // Verify that constructors throw an exception when invalid option
+    // space name has been specified.
+    EXPECT_THROW(OptionSpace6("isc dhcp"), InvalidOptionSpace);
+    EXPECT_THROW(OptionSpace6("isc%dhcp", 2145), InvalidOptionSpace);
+}
+
+// The purpose of this test is to verify an option space can be marked
+// vendor option space and enterprise number can be set.
+TEST(OptionSpace6Test, setVendorSpace) {
+    OptionSpace6 space("isc");
+    EXPECT_EQ("isc", space.getName());
+    EXPECT_FALSE(space.isVendorSpace());
+
+    // Mark it vendor option space and set enterprise id.
+    space.setVendorSpace(1234);
+    EXPECT_TRUE(space.isVendorSpace());
+    EXPECT_EQ(1234, space.getEnterpriseNumber());
+
+    // Override the enterprise number to make sure and make sure that
+    // the new number is returned by the object.
+    space.setVendorSpace(2345);
+    EXPECT_TRUE(space.isVendorSpace());
+    EXPECT_EQ(2345, space.getEnterpriseNumber());
+
+    // Clear the vendor option space flag.
+    space.clearVendorSpace();
+    EXPECT_FALSE(space.isVendorSpace());
+}
+
+
+}; // end of anonymous namespace
diff --git a/src/lib/dhcpsrv/Makefile.am b/src/lib/dhcpsrv/Makefile.am
index 0b02ef8..e721267 100644
--- a/src/lib/dhcpsrv/Makefile.am
+++ b/src/lib/dhcpsrv/Makefile.am
@@ -43,7 +43,6 @@ libb10_dhcpsrv_la_SOURCES += memfile_lease_mgr.cc memfile_lease_mgr.h
 if HAVE_MYSQL
 libb10_dhcpsrv_la_SOURCES += mysql_lease_mgr.cc mysql_lease_mgr.h
 endif
-libb10_dhcpsrv_la_SOURCES += option_space.cc option_space.h
 libb10_dhcpsrv_la_SOURCES += option_space_container.h
 libb10_dhcpsrv_la_SOURCES += pool.cc pool.h
 libb10_dhcpsrv_la_SOURCES += subnet.cc subnet.h
diff --git a/src/lib/dhcpsrv/cfgmgr.h b/src/lib/dhcpsrv/cfgmgr.h
index 0e56869..f4eecb5 100644
--- a/src/lib/dhcpsrv/cfgmgr.h
+++ b/src/lib/dhcpsrv/cfgmgr.h
@@ -18,7 +18,7 @@
 #include <asiolink/io_address.h>
 #include <dhcp/option.h>
 #include <dhcp/option_definition.h>
-#include <dhcpsrv/option_space.h>
+#include <dhcp/option_space.h>
 #include <dhcpsrv/option_space_container.h>
 #include <dhcpsrv/pool.h>
 #include <dhcpsrv/subnet.h>
diff --git a/src/lib/dhcpsrv/option_space.cc b/src/lib/dhcpsrv/option_space.cc
deleted file mode 100644
index 0e802a7..0000000
--- a/src/lib/dhcpsrv/option_space.cc
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright (C) 2012, 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 <dhcpsrv/option_space.h>
-#include <boost/algorithm/string/classification.hpp>
-#include <boost/algorithm/string/predicate.hpp>
-
-namespace isc {
-namespace dhcp {
-
-OptionSpace::OptionSpace(const std::string& name, const bool vendor_space)
-    : name_(name), vendor_space_(vendor_space) {
-    //  Check that provided option space name is valid.
-    if (!validateName(name_)) {
-        isc_throw(InvalidOptionSpace, "Invalid option space name "
-                  << name_);
-    }
-}
-
-bool
-OptionSpace::validateName(const std::string& name) {
-
-    using namespace boost::algorithm;
-
-    // Allowed characters are: lower or upper case letters, digits,
-    // underscores and hyphens. Empty option spaces are not allowed.
-    if (all(name, boost::is_from_range('a', 'z') ||
-            boost::is_from_range('A', 'Z') ||
-            boost::is_digit() ||
-            boost::is_any_of(std::string("-_"))) &&
-        !name.empty() &&
-        // Hyphens and underscores are not allowed at the beginning
-        // and at the end of the option space name.
-        !all(find_head(name, 1), boost::is_any_of(std::string("-_"))) &&
-        !all(find_tail(name, 1), boost::is_any_of(std::string("-_")))) {
-        return (true);
-
-    }
-    return (false);
-}
-
-OptionSpace6::OptionSpace6(const std::string& name)
-    : OptionSpace(name),
-      enterprise_number_(0) {
-}
-
-OptionSpace6::OptionSpace6(const std::string& name,
-                           const uint32_t enterprise_number)
-    : OptionSpace(name, true),
-      enterprise_number_(enterprise_number) {
-}
-
-void
-OptionSpace6::setVendorSpace(const uint32_t enterprise_number) {
-    enterprise_number_ = enterprise_number;
-    OptionSpace::setVendorSpace();
-}
-
-} // end of isc::dhcp namespace
-} // end of isc namespace
diff --git a/src/lib/dhcpsrv/option_space.h b/src/lib/dhcpsrv/option_space.h
deleted file mode 100644
index 9eebd76..0000000
--- a/src/lib/dhcpsrv/option_space.h
+++ /dev/null
@@ -1,189 +0,0 @@
-// Copyright (C) 2012, 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 OPTION_SPACE_H
-#define OPTION_SPACE_H
-
-#include <exceptions/exceptions.h>
-#include <boost/shared_ptr.hpp>
-#include <map>
-#include <stdint.h>
-#include <string>
-
-namespace isc {
-namespace dhcp {
-
-/// @brief Exception to be thrown when invalid option space
-/// is specified.
-class InvalidOptionSpace : public Exception {
-public:
-    InvalidOptionSpace(const char* file, size_t line, const char* what) :
-        isc::Exception(file, line, what) { };
-};
-
-/// OptionSpace forward declaration.
-class OptionSpace;
-/// A pointer to OptionSpace object.
-typedef boost::shared_ptr<OptionSpace> OptionSpacePtr;
-/// A collection of option spaces.
-typedef std::map<std::string, OptionSpacePtr> OptionSpaceCollection;
-
-/// @brief DHCP option space.
-///
-/// This class represents single option space. The option spaces are used
-/// to group DHCP options having unique option codes. The special type
-/// of the option space is so called "vendor specific option space".
-/// It groups sub-options being sent within Vendor Encapsulated Options.
-/// For DHCPv4 it is the option with code 43. The option spaces are
-/// assigned to option instances represented by isc::dhcp::Option and
-/// other classes derived from it. Each particular option may belong to
-/// multiple option spaces.
-/// This class may be used to represent any DHCPv4 option space. If the
-/// option space is to group DHCPv4 Vendor Encapsulated Options then
-/// "vendor space" flag must be set using \ref OptionSpace::setVendorSpace
-/// or the argument passed to the constructor. In theory, this class can
-/// be also used to represent non-vendor specific DHCPv6 option space
-/// but this is discouraged. For DHCPv6 option spaces the OptionSpace6
-/// class should be used instead.
-///
-/// @note this class is intended to be used to represent DHCPv4 option
-/// spaces only. However, it hasn't been called OptionSpace4 (that would
-/// suggest that it is specific to DHCPv4) because it can be also
-/// used to represent some DHCPv6 option spaces and is a base class
-/// for \ref OptionSpace6. Thus, if one declared the container as follows:
-/// @code
-/// std::vector<OptionSpace4> container;
-/// @endcode
-/// it would suggest that the container holds DHCPv4 option spaces while
-/// it could hold both DHCPv4 and DHCPv6 option spaces as the OptionSpace6
-/// object could be upcast to OptionSpace4. This confusion does not appear
-/// when OptionSpace is used as a name for the base class.
-class OptionSpace {
-public:
-
-    /// @brief Constructor.
-    ///
-    /// @param name option space name.
-    /// @param vendor_space boolean value that indicates that the object
-    /// describes the vendor specific option space.
-    ///
-    /// @throw isc::dhcp::InvalidOptionSpace if given option space name
-    /// contains invalid characters or is empty. This constructor uses
-    /// \ref validateName function to check that the specified name is
-    /// correct.
-    OptionSpace(const std::string& name, const bool vendor_space = false);
-
-    /// @brief Return option space name.
-    ///
-    /// @return option space name.
-    const std::string& getName() const { return (name_); }
-
-    /// @brief Mark option space as non-vendor space.
-    void clearVendorSpace() {
-        vendor_space_ = false;
-    }
-
-    /// @brief Check if option space is vendor specific.
-    ///
-    /// @return boolean value that indicates if the object describes
-    /// the vendor specific option space.
-    bool isVendorSpace() const { return (vendor_space_); }
-
-    /// @brief Mark option space as vendor specific.
-    void setVendorSpace() {
-        vendor_space_ = true;
-    }
-
-    /// @brief Checks that the provided option space name is valid.
-    ///
-    /// It is expected that option space name consists of upper or
-    /// lower case letters or digits. Also, it may contain underscores
-    /// or dashes. Other characters are prohibited. The empty option
-    /// space names are invalid.
-    ///
-    /// @param name option space name to be validated.
-    ///
-    /// @return true if the option space is valid, else it returns false.
-    static bool validateName(const std::string& name);
-
-private:
-    std::string name_;  ///< Holds option space name.
-
-    bool vendor_space_; ///< Is this the vendor space?
-
-};
-
-/// @brief DHCPv6 option space with enterprise number assigned.
-///
-/// This class extends the base class with the support for enterprise numbers.
-/// The enterprise numbers are assigned by IANA to various organizations
-/// and they are carried as uint32_t integers in DHCPv6 Vendor Specific
-/// Information Options (VSIO). For more information refer to RFC3315.
-/// All option spaces that group VSIO options must have enterprise number
-/// set. It can be set using a constructor or \ref setVendorSpace function.
-/// The extra functionality of this class (enterprise numbers) allows to
-/// represent DHCPv6 vendor-specific option spaces but this class is also
-/// intended to be used for all other DHCPv6 option spaces. That way all
-/// DHCPv6 option spaces can be stored in the container holding OptionSpace6
-/// objects. Also, it is easy to mark vendor-specific option space as non-vendor
-/// specific option space (and the other way around) without a need to cast
-/// between OptionSpace and OptionSpace6 types.
-class OptionSpace6 : public OptionSpace {
-public:
-
-    /// @brief Constructor for non-vendor-specific options.
-    ///
-    /// This constructor marks option space as non-vendor specific.
-    ///
-    /// @param name option space name.
-    ///
-    /// @throw isc::dhcp::InvalidOptionSpace if given option space name
-    /// contains invalid characters or is empty. This constructor uses
-    /// \ref OptionSpace::validateName function to check that the specified
-    /// name is correct.
-    OptionSpace6(const std::string& name);
-
-    /// @brief Constructor for vendor-specific options.
-    ///
-    /// This constructor marks option space as vendor specific and sets
-    /// enterprise number to a given value.
-    ///
-    /// @param name option space name.
-    /// @param enterprise_number enterprise number.
-    ///
-    /// @throw isc::dhcp::InvalidOptionSpace if given option space name
-    /// contains invalid characters or is empty. This constructor uses
-    /// \ref OptionSpace::validateName function to check that the specified
-    /// name is correct.
-    OptionSpace6(const std::string& name, const uint32_t enterprise_number);
-
-    /// @brief Return enterprise number for the option space.
-    ///
-    /// @return enterprise number.
-    uint32_t getEnterpriseNumber() const { return (enterprise_number_); }
-
-    /// @brief Mark option space as vendor specific.
-    ///
-    /// @param enterprise_number enterprise number.
-    void setVendorSpace(const uint32_t enterprise_number);
-
-private:
-    
-    uint32_t enterprise_number_; ///< IANA assigned enterprise number.
-};
-
-} // namespace isc::dhcp
-} // namespace isc
-
-#endif // OPTION_SPACE_H
diff --git a/src/lib/dhcpsrv/tests/Makefile.am b/src/lib/dhcpsrv/tests/Makefile.am
index 00476c0..e19fd87 100644
--- a/src/lib/dhcpsrv/tests/Makefile.am
+++ b/src/lib/dhcpsrv/tests/Makefile.am
@@ -37,7 +37,6 @@ libdhcpsrv_unittests_SOURCES += memfile_lease_mgr_unittest.cc
 if HAVE_MYSQL
 libdhcpsrv_unittests_SOURCES += mysql_lease_mgr_unittest.cc
 endif
-libdhcpsrv_unittests_SOURCES += option_space_unittest.cc
 libdhcpsrv_unittests_SOURCES += pool_unittest.cc
 libdhcpsrv_unittests_SOURCES += schema_copy.h
 libdhcpsrv_unittests_SOURCES += subnet_unittest.cc
diff --git a/src/lib/dhcpsrv/tests/option_space_unittest.cc b/src/lib/dhcpsrv/tests/option_space_unittest.cc
deleted file mode 100644
index f8d75c8..0000000
--- a/src/lib/dhcpsrv/tests/option_space_unittest.cc
+++ /dev/null
@@ -1,150 +0,0 @@
-// Copyright (C) 2012, 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 <dhcpsrv/option_space.h>
-
-#include <gtest/gtest.h>
-
-using namespace isc::dhcp;
-using namespace isc;
-
-namespace {
-
-// The purpose of this test is to verify that the constructor
-// creates an object with members initialized to correct values.
-TEST(OptionSpaceTest, constructor) {
-    // Create some option space.
-    OptionSpace space("isc", true);
-    EXPECT_EQ("isc", space.getName());
-    EXPECT_TRUE(space.isVendorSpace());
-
-    // Create another object with different values
-    // to check that the values will change.
-    OptionSpace space2("abc", false);
-    EXPECT_EQ("abc", space2.getName());
-    EXPECT_FALSE(space2.isVendorSpace());
-
-    // Verify that constructor throws exception if invalid
-    // option space name is provided.
-    EXPECT_THROW(OptionSpace("invalid%space.name"), InvalidOptionSpace);
-}
-
-// The purpose of this test is to verify that the vendor-space flag
-// can be overriden.
-TEST(OptionSpaceTest, setVendorSpace) {
-    OptionSpace space("isc", true);
-    EXPECT_EQ("isc", space.getName());
-    EXPECT_TRUE(space.isVendorSpace());
-
-    // Override the vendor space flag.
-    space.clearVendorSpace();
-    EXPECT_FALSE(space.isVendorSpace());
-}
-
-// The purpose of this test is to verify that the static function
-// to validate the option space name works correctly.
-TEST(OptionSpaceTest, validateName) {
-    // Positive test scenarios: letters, digits, dashes, underscores
-    // lower/upper case allowed.
-    EXPECT_TRUE(OptionSpace::validateName("abc"));
-    EXPECT_TRUE(OptionSpace::validateName("dash-allowed"));
-    EXPECT_TRUE(OptionSpace::validateName("two-dashes-allowed"));
-    EXPECT_TRUE(OptionSpace::validateName("underscore_allowed"));
-    EXPECT_TRUE(OptionSpace::validateName("underscore_three_times_allowed"));
-    EXPECT_TRUE(OptionSpace::validateName("digits0912"));
-    EXPECT_TRUE(OptionSpace::validateName("1234"));
-    EXPECT_TRUE(OptionSpace::validateName("UPPER_CASE_allowed"));
-
-    // Negative test scenarions: empty strings, dots, spaces are not
-    // allowed
-    EXPECT_FALSE(OptionSpace::validateName(""));
-    EXPECT_FALSE(OptionSpace::validateName(" "));
-    EXPECT_FALSE(OptionSpace::validateName(" isc "));
-    EXPECT_FALSE(OptionSpace::validateName("isc "));
-    EXPECT_FALSE(OptionSpace::validateName(" isc"));
-    EXPECT_FALSE(OptionSpace::validateName("isc with-space"));
-
-    // Hyphens and underscores are not allowed at the beginning
-    // and at the end of the option space name.
-    EXPECT_FALSE(OptionSpace::validateName("-isc"));
-    EXPECT_FALSE(OptionSpace::validateName("isc-"));
-    EXPECT_FALSE(OptionSpace::validateName("_isc"));
-    EXPECT_FALSE(OptionSpace::validateName("isc_"));
-
-    // Test other special characters
-    const char specials[] = { '!', '@', '#', '$', '%', '^', '&', '*', '(', ')',
-                              '+', '=', '[', ']', '{', '}', ';', ':', '"', '\'',
-                              '\\', '|', '<','>', ',', '.', '?', '~', '`' };
-    for (int i = 0; i < sizeof(specials); ++i) {
-        std::ostringstream stream;
-        // Concatenate valid option space name: "abc" with an invalid character.
-        // That way we get option space names like: "abc!", "abc$" etc. It is
-        // expected that the validating function fails form them.
-        stream << "abc" << specials[i];
-        EXPECT_FALSE(OptionSpace::validateName(stream.str()))
-            << "Test failed for special character '" << specials[i] << "'.";
-    }
-}
-
-// The purpose of this test is to verify that the constructors of the
-// OptionSpace6 class set the class members to correct values.
-TEST(OptionSpace6Test, constructor) {
-    // Create some option space and do not specify enterprise number.
-    // In such case the vendor space flag is expected to be
-    // set to false.
-    OptionSpace6 space1("abcd");
-    EXPECT_EQ("abcd", space1.getName());
-    EXPECT_FALSE(space1.isVendorSpace());
-
-    // Create an option space and specify an enterprise number. In this
-    // case the vendor space flag is expected to be set to true and the
-    // enterprise number should be set to a desired value.
-    OptionSpace6 space2("abcd", 2145);
-    EXPECT_EQ("abcd", space2.getName());
-    EXPECT_TRUE(space2.isVendorSpace());
-    EXPECT_EQ(2145, space2.getEnterpriseNumber());
-
-    // Verify that constructors throw an exception when invalid option
-    // space name has been specified.
-    EXPECT_THROW(OptionSpace6("isc dhcp"), InvalidOptionSpace);
-    EXPECT_THROW(OptionSpace6("isc%dhcp", 2145), InvalidOptionSpace);
-}
-
-// The purpose of this test is to verify an option space can be marked
-// vendor option space and enterprise number can be set.
-TEST(OptionSpace6Test, setVendorSpace) {
-    OptionSpace6 space("isc");
-    EXPECT_EQ("isc", space.getName());
-    EXPECT_FALSE(space.isVendorSpace());
-
-    // Mark it vendor option space and set enterprise id.
-    space.setVendorSpace(1234);
-    EXPECT_TRUE(space.isVendorSpace());
-    EXPECT_EQ(1234, space.getEnterpriseNumber());
-
-    // Override the enterprise number to make sure and make sure that
-    // the new number is returned by the object.
-    space.setVendorSpace(2345);
-    EXPECT_TRUE(space.isVendorSpace());
-    EXPECT_EQ(2345, space.getEnterpriseNumber());
-
-    // Clear the vendor option space flag.
-    space.clearVendorSpace();
-    EXPECT_FALSE(space.isVendorSpace());
-}
-
-
-}; // end of anonymous namespace



More information about the bind10-changes mailing list