Browse Source

[master] Merge branch 'trac2417'

Marcin Siodelski 12 years ago
parent
commit
b492185931

+ 48 - 10
src/bin/dhcp6/config_parser.cc

@@ -26,6 +26,7 @@
 #include <cc/data.h>
 #include <cc/data.h>
 #include <config/ccsession.h>
 #include <config/ccsession.h>
 #include <log/logger_support.h>
 #include <log/logger_support.h>
+#include <dhcp/libdhcp++.h>
 #include <dhcp/triplet.h>
 #include <dhcp/triplet.h>
 #include <dhcp/pool.h>
 #include <dhcp/pool.h>
 #include <dhcp/subnet.h>
 #include <dhcp/subnet.h>
@@ -616,7 +617,11 @@ private:
                       << " spaces");
                       << " spaces");
         }
         }
 
 
+        // Get option data from the configuration database ('data' field).
+        // Option data is specified by the user as case insensitive string
+        // of hexadecimal digits for each option.
         std::string option_data = getStringParam("data");
         std::string option_data = getStringParam("data");
+        // Transform string of hexadecimal digits into binary format.
         std::vector<uint8_t> binary;
         std::vector<uint8_t> binary;
         try {
         try {
             util::encode::decodeHex(option_data, binary);
             util::encode::decodeHex(option_data, binary);
@@ -624,16 +629,49 @@ private:
             isc_throw(Dhcp6ConfigError, "Parser error: option data is not a valid"
             isc_throw(Dhcp6ConfigError, "Parser error: option data is not a valid"
                       << " string of hexadecimal digits: " << option_data);
                       << " string of hexadecimal digits: " << option_data);
         }
         }
-
-        // Create the actual option.
-        // @todo Currently we simply create dhcp::Option instance here but we will
-        // need to use dedicated factory functions once the option definitions are
-        // created for all options.
-        OptionPtr option(new Option(Option::V6, static_cast<uint16_t>(option_code),
-                                    binary));
-
-        // If option is created succesfully, add it to the storage.
-        options_->push_back(option);
+        // Get all existing DHCPv6 option definitions. The one that matches
+        // our option will be picked and used to create it.
+        OptionDefContainer option_defs = LibDHCP::getOptionDefs(Option::V6);
+        // Get search index #1. It allows searching for options definitions
+        // using option type value.
+        const OptionDefContainerTypeIndex& idx = option_defs.get<1>();
+        // Get all option definitions matching option code we want to create.
+        const OptionDefContainerTypeRange& range = idx.equal_range(option_code);
+        size_t num_defs = std::distance(range.first, range.second);
+        OptionPtr option;
+        // Currently we do not allow duplicated definitions and if there are
+        // any duplicates we issue internal server error.
+        if (num_defs > 1) {
+            isc_throw(Dhcp6ConfigError, "Internal error: currently it is not"
+                      << " supported to initialize multiple option definitions"
+                      << " for the same option code. This will be supported once"
+                      << " there option spaces are implemented.");
+        } else if (num_defs == 0) {
+            // @todo We have a limited set of option definitions intiialized at the moment.
+            // In the future we want to initialize option definitions for all options.
+            // Consequently error will be issued if option definition does not exist
+            // for a particular option code. For now it is ok to create generic option
+            // if definition does not exist.
+            OptionPtr option(new Option(Option::V6, static_cast<uint16_t>(option_code),
+                                        binary));
+            // If option is created succesfully, add it to the storage.
+            options_->push_back(option);
+        } else {
+            // We have exactly one option definition for the particular option code.
+            // use it to create option instance.
+            const OptionDefinitionPtr& def = *(range.first);
+            // getFactory should never return NULL pointer.
+            Option::Factory* factory = def->getFactory();
+            assert(factory != NULL);
+            try {
+                OptionPtr option = factory(Option::V6, option_code, binary);
+                options_->push_back(option);
+            } catch (const isc::Exception& ex) {
+                isc_throw(Dhcp6ConfigError, "Parser error: option data does not match"
+                          << " option definition (code " << option_code << "): "
+                          << ex.what());
+            }
+        }
     }
     }
 
 
     /// @brief Get a parameter from the strings storage.
     /// @brief Get a parameter from the strings storage.

+ 10 - 0
src/bin/dhcp6/dhcp6_messages.mes

@@ -110,6 +110,16 @@ This is a debug message issued during the IPv6 DHCP server startup.
 It lists some information about the parameters with which the server
 It lists some information about the parameters with which the server
 is running.
 is running.
 
 
+% DHCP6_NO_SUBNET_DEF_OPT failed to find subnet for address %1 when adding default options
+This warning message indicates that when attempting to add default options to a response,
+the server found that it was not configured to support the subnet from which the DHCPv6
+request was received.  The packet has been ignored.
+
+% DHCP6_NO_SUBNET_REQ_OPT failed to find subnet for address %1 when adding requested options
+This warning message indicates that when attempting to add requested options to a response,
+the server found that it was not configured to support the subnet from which the DHCPv6
+request was received.  The packet has been ignored.
+
 % DHCP6_CONFIG_LOAD_FAIL failed to load configuration: %1
 % DHCP6_CONFIG_LOAD_FAIL failed to load configuration: %1
 This critical error message indicates that the initial DHCPv6
 This critical error message indicates that the initial DHCPv6
 configuration has failed. The server will start, but nothing will be
 configuration has failed. The server will start, but nothing will be

+ 72 - 14
src/bin/dhcp6/dhcp6_srv.cc

@@ -20,14 +20,20 @@
 #include <dhcp6/dhcp6_srv.h>
 #include <dhcp6/dhcp6_srv.h>
 #include <dhcp/dhcp6.h>
 #include <dhcp/dhcp6.h>
 #include <dhcp/iface_mgr.h>
 #include <dhcp/iface_mgr.h>
+#include <dhcp/libdhcp++.h>
 #include <dhcp/option6_addrlst.h>
 #include <dhcp/option6_addrlst.h>
 #include <dhcp/option6_iaaddr.h>
 #include <dhcp/option6_iaaddr.h>
 #include <dhcp/option6_ia.h>
 #include <dhcp/option6_ia.h>
+#include <dhcp/option6_int_array.h>
 #include <dhcp/pkt6.h>
 #include <dhcp/pkt6.h>
+#include <dhcp/subnet.h>
+#include <dhcp/cfgmgr.h>
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
 #include <util/io_utilities.h>
 #include <util/io_utilities.h>
 #include <util/range_utilities.h>
 #include <util/range_utilities.h>
 
 
+#include <boost/foreach.hpp>
+
 using namespace isc;
 using namespace isc;
 using namespace isc::asiolink;
 using namespace isc::asiolink;
 using namespace isc::dhcp;
 using namespace isc::dhcp;
@@ -51,10 +57,17 @@ Dhcpv6Srv::Dhcpv6Srv(uint16_t port) {
 
 
     LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_OPEN_SOCKET).arg(port);
     LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_OPEN_SOCKET).arg(port);
 
 
-    // First call to instance() will create IfaceMgr (it's a singleton)
-    // it may throw something if things go wrong
+    // Initialize objects required for DHCP server operation.
     try {
     try {
 
 
+        // Initialize standard DHCPv6 option definitions. This function
+        // may throw bad_alloc if system goes out of memory during the
+        // creation if option definitions. It may also throw isc::Unexpected
+        // if definitions are wrong. This would mean error in implementation.
+        initStdOptionDefs();
+
+        // Call IfaceMgr::instance() will create instance of Interface
+        // Manager (it's a singleton). It may throw if things go wrong.
         if (IfaceMgr::instance().countIfaces() == 0) {
         if (IfaceMgr::instance().countIfaces() == 0) {
             LOG_ERROR(dhcp6_logger, DHCP6_NO_INTERFACES);
             LOG_ERROR(dhcp6_logger, DHCP6_NO_INTERFACES);
             shutdown_ = true;
             shutdown_ = true;
@@ -281,23 +294,63 @@ void Dhcpv6Srv::copyDefaultOptions(const Pkt6Ptr& question, Pkt6Ptr& answer) {
     // TODO: Should throw if there is no client-id (except anonymous INF-REQUEST)
     // TODO: Should throw if there is no client-id (except anonymous INF-REQUEST)
 }
 }
 
 
-void Dhcpv6Srv::appendDefaultOptions(const Pkt6Ptr& /*question*/, Pkt6Ptr& answer) {
-    // TODO: question is currently unused, but we need it at least to know
-    // message type we are answering
-
+void Dhcpv6Srv::appendDefaultOptions(const Pkt6Ptr& question, Pkt6Ptr& answer) {
     // add server-id
     // add server-id
     answer->addOption(getServerID());
     answer->addOption(getServerID());
-}
 
 
+    // Get the subnet object. It holds options to be sent to the client
+    // that belongs to the particular subnet.
+    Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(question->getRemoteAddr());
+    // Warn if subnet is not supported and quit.
+    if (!subnet) {
+        LOG_WARN(dhcp6_logger, DHCP6_NO_SUBNET_DEF_OPT)
+            .arg(question->getRemoteAddr().toText());
+        return;
+    }
+    // Add DNS_SERVERS option. It should have been configured.
+    const Subnet::OptionContainer& options = subnet->getOptions();
+    const Subnet::OptionContainerTypeIndex& idx = options.get<1>();
+    const Subnet::OptionContainerTypeRange range =
+        idx.equal_range(D6O_NAME_SERVERS);
+    // In theory we may have multiple options with the same
+    // option code. They are not differentiated right now
+    // until support for option spaces is implemented.
+    // Until that's the case, simply add the first found option.
+    if (std::distance(range.first, range.second) > 0) {
+        answer->addOption(range.first->option);
+    }
+}
 
 
-void Dhcpv6Srv::appendRequestedOptions(const Pkt6Ptr& /*question*/, Pkt6Ptr& answer) {
-    // TODO: question is currently unused, but we need to extract ORO from it
-    // and act on its content. Now we just send DNS-SERVERS option.
+void Dhcpv6Srv::appendRequestedOptions(const Pkt6Ptr& question, Pkt6Ptr& answer) {
+    // Get the subnet for a particular address.
+    Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(question->getRemoteAddr());
+    if (!subnet) {
+        LOG_WARN(dhcp6_logger, DHCP6_NO_SUBNET_REQ_OPT)
+            .arg(question->getRemoteAddr().toText());
+        return;
+    }
 
 
-    // add dns-servers option
-    boost::shared_ptr<Option> dnsservers(new Option6AddrLst(D6O_NAME_SERVERS,
-                                         IOAddress(HARDCODED_DNS_SERVER)));
-    answer->addOption(dnsservers);
+    // Client requests some options using ORO option. Try to
+    // get this option from client's message.
+    boost::shared_ptr<Option6IntArray<uint16_t> > option_oro =
+        boost::dynamic_pointer_cast<Option6IntArray<uint16_t> >(question->getOption(D6O_ORO));
+    // Option ORO not found. Don't do anything then.
+    if (!option_oro) {
+        return;
+    }
+    // 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.
+    const Subnet::OptionContainer& options = subnet->getOptions();
+    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) {
+            answer->addOption(desc.option);
+        }
+    }
 }
 }
 
 
 void Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer) {
 void Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer) {
@@ -428,3 +481,8 @@ Dhcpv6Srv::serverReceivedPacketName(uint8_t type) {
     }
     }
     return (UNKNOWN);
     return (UNKNOWN);
 }
 }
+
+void
+Dhcpv6Srv::initStdOptionDefs() {
+    LibDHCP::initStdOptionDefs(Option::V6);
+}

+ 13 - 2
src/bin/dhcp6/dhcp6_srv.h

@@ -19,6 +19,7 @@
 #include <dhcp/dhcp6.h>
 #include <dhcp/dhcp6.h>
 #include <dhcp/pkt6.h>
 #include <dhcp/pkt6.h>
 #include <dhcp/option.h>
 #include <dhcp/option.h>
+#include <dhcp/option_definition.h>
 #include <iostream>
 #include <iostream>
 
 
 namespace isc {
 namespace isc {
@@ -170,8 +171,6 @@ protected:
     /// @brief Appends requested options to server's answer.
     /// @brief Appends requested options to server's answer.
     ///
     ///
     /// Appends options requested by client to the server's answer.
     /// Appends options requested by client to the server's answer.
-    /// TODO: This method is currently a stub. It just appends DNS-SERVERS
-    /// option.
     ///
     ///
     /// @param question client's message
     /// @param question client's message
     /// @param answer server's message (options will be added here)
     /// @param answer server's message (options will be added here)
@@ -199,6 +198,18 @@ protected:
     ///         interfaces for new DUID generation are detected.
     ///         interfaces for new DUID generation are detected.
     void setServerID();
     void setServerID();
 
 
+    /// @brief Initializes option definitions for standard options.
+    ///
+    /// Each standard option's format is described by the
+    /// dhcp::OptionDefinition object. This function creates such objects
+    /// for each standard DHCPv6 option.
+    ///
+    /// @todo list thrown exceptions.
+    /// @todo extend this function to cover all standard options. Currently
+    /// it is limited to critical options only.
+    void initStdOptionDefs();
+
+private:
     /// server DUID (to be sent in server-identifier option)
     /// server DUID (to be sent in server-identifier option)
     boost::shared_ptr<isc::dhcp::Option> serverid_;
     boost::shared_ptr<isc::dhcp::Option> serverid_;
 
 

+ 95 - 15
src/bin/dhcp6/tests/config_parser_unittest.cc

@@ -17,14 +17,18 @@
 #include <fstream>
 #include <fstream>
 #include <sstream>
 #include <sstream>
 
 
+#include <boost/foreach.hpp>
+
 #include <arpa/inet.h>
 #include <arpa/inet.h>
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
 
 
 #include <dhcp6/dhcp6_srv.h>
 #include <dhcp6/dhcp6_srv.h>
 #include <dhcp6/config_parser.h>
 #include <dhcp6/config_parser.h>
 #include <config/ccsession.h>
 #include <config/ccsession.h>
+#include <dhcp/libdhcp++.h>
 #include <dhcp/subnet.h>
 #include <dhcp/subnet.h>
 #include <dhcp/cfgmgr.h>
 #include <dhcp/cfgmgr.h>
+#include <dhcp/option6_ia.h>
 
 
 using namespace std;
 using namespace std;
 using namespace isc;
 using namespace isc;
@@ -43,6 +47,10 @@ public:
         // deal with sockets here, just check if configuration handling
         // deal with sockets here, just check if configuration handling
         // is sane.
         // is sane.
         srv_ = new Dhcpv6Srv(0);
         srv_ = new Dhcpv6Srv(0);
+        // Create instances of option definitions and put them into storage.
+        // This is normally initialized by the server when calling run()
+        // run() function.
+        LibDHCP::initStdOptionDefs(Option::V6);
     }
     }
 
 
     ~Dhcp6ParserTest() {
     ~Dhcp6ParserTest() {
@@ -60,6 +68,24 @@ public:
     /// param value.
     /// param value.
     std::string createConfigWithOption(const std::string& param_value,
     std::string createConfigWithOption(const std::string& param_value,
                                        const std::string& parameter) {
                                        const std::string& parameter) {
+        std::map<std::string, std::string> params;
+        if (parameter == "name") {
+            params["name"] = param_value;
+            params["code"] = "80";
+            params["data"] = "AB CDEF0105";
+        } else if (parameter == "code") {
+            params["name"] = "option_foo";
+            params["code"] = param_value;
+            params["data"] = "AB CDEF0105";
+        } else if (parameter == "data") {
+            params["name"] = "option_foo";
+            params["code"] = "80";
+            params["data"] = param_value;
+        }
+        return (createConfigWithOption(params));
+    }
+
+    std::string createConfigWithOption(const std::map<std::string, std::string>& params) {
         std::ostringstream stream;
         std::ostringstream stream;
         stream << "{ \"interface\": [ \"all\" ],"
         stream << "{ \"interface\": [ \"all\" ],"
             "\"preferred-lifetime\": 3000,"
             "\"preferred-lifetime\": 3000,"
@@ -69,21 +95,21 @@ public:
             "    \"pool\": [ \"2001:db8:1::/80\" ],"
             "    \"pool\": [ \"2001:db8:1::/80\" ],"
             "    \"subnet\": \"2001:db8:1::/64\", "
             "    \"subnet\": \"2001:db8:1::/64\", "
             "    \"option-data\": [ {";
             "    \"option-data\": [ {";
-        if (parameter == "name") {
-            stream <<
-                "          \"name\": \"" << param_value << "\","
-                "          \"code\": 80,"
-                "          \"data\": \"AB CDEF0105\"";
-        } else if (parameter == "code") {
-            stream <<
-                "          \"name\": \"option_foo\","
-                "          \"code\": " << param_value << ","
-                "          \"data\": \"AB CDEF0105\"";
-        } else if (parameter == "data") {
-            stream <<
-                "          \"name\": \"option_foo\","
-                "          \"code\": 80,"
-                "          \"data\": \"" << param_value << "\"";
+        bool first = true;
+        typedef std::pair<std::string, std::string> ParamPair;
+        BOOST_FOREACH(ParamPair param, params) {
+            if (!first) {
+                stream << ", ";
+            } else {
+                first = false;
+            }
+            if (param.first == "name") {
+                stream << "\"name\": \"" << param.second << "\"";
+            } else if (param.first == "code") {
+                stream << "\"code\": " << param.second << "";
+            } else if (param.first == "data") {
+                stream << "\"data\": \"" << param.second << "\"";
+            }
         }
         }
         stream <<
         stream <<
             "        } ]"
             "        } ]"
@@ -658,4 +684,58 @@ TEST_F(Dhcp6ParserTest, optionDataLowerCase) {
     testOption(*range.first, 80, foo_expected, sizeof(foo_expected));
     testOption(*range.first, 80, foo_expected, sizeof(foo_expected));
 }
 }
 
 
+// Verify that specific option object is returned for standard
+// option which has dedicated option class derived from Option.
+TEST_F(Dhcp6ParserTest, stdOptionData) {
+    ConstElementPtr x;
+    std::map<std::string, std::string> params;
+    params["name"] = "OPTION_IA_NA";
+    // Option code 3 means OPTION_IA_NA.
+    params["code"] = "3";
+    params["data"] = "ABCDEF01 02030405 06070809";
+    
+    std::string config = createConfigWithOption(params);
+    ElementPtr json = Element::fromJSON(config);
+
+    EXPECT_NO_THROW(x = configureDhcp6Server(*srv_, json));
+    ASSERT_TRUE(x);
+    comment_ = parseAnswer(rcode_, x);
+    ASSERT_EQ(0, rcode_);
+
+    Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"));
+    ASSERT_TRUE(subnet);
+    const Subnet::OptionContainer& options = subnet->getOptions();
+    ASSERT_EQ(1, 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_IA_NA);
+    // Expect single option with the code equal to IA_NA option code.
+    ASSERT_EQ(1, std::distance(range.first, range.second));
+    // The actual pointer to the option is held in the option field
+    // in the structure returned.
+    OptionPtr option = range.first->option;
+    ASSERT_TRUE(option);
+    // Option object returned for here is expected to be Option6IA
+    // which is derived from Option. This class is dedicated to
+    // represent standard option IA_NA.
+    boost::shared_ptr<Option6IA> optionIA =
+        boost::dynamic_pointer_cast<Option6IA>(option);
+    // If cast is unsuccessful than option returned was of a
+    // differnt type than Option6IA. This is wrong.
+    ASSERT_TRUE(optionIA);
+    // If cast was successful we may use accessors exposed by
+    // Option6IA to validate that the content of this option
+    // has been set correctly.
+    EXPECT_EQ(0xABCDEF01, optionIA->getIAID());
+    EXPECT_EQ(0x02030405, optionIA->getT1());
+    EXPECT_EQ(0x06070809, optionIA->getT2());
+}
+
 };
 };

+ 114 - 25
src/bin/dhcp6/tests/dhcp6_srv_unittest.cc

@@ -20,9 +20,13 @@
 #include <arpa/inet.h>
 #include <arpa/inet.h>
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
 
 
+#include <dhcp6/config_parser.h>
+#include <dhcp6/dhcp6_srv.h>
 #include <dhcp/dhcp6.h>
 #include <dhcp/dhcp6.h>
 #include <dhcp/option6_ia.h>
 #include <dhcp/option6_ia.h>
-#include <dhcp6/dhcp6_srv.h>
+#include <dhcp/option6_addrlst.h>
+#include <dhcp/option6_int_array.h>
+#include <config/ccsession.h>
 #include <util/buffer.h>
 #include <util/buffer.h>
 #include <util/range_utilities.h>
 #include <util/range_utilities.h>
 #include <boost/scoped_ptr.hpp>
 #include <boost/scoped_ptr.hpp>
@@ -31,6 +35,9 @@ using namespace std;
 using namespace isc;
 using namespace isc;
 using namespace isc::dhcp;
 using namespace isc::dhcp;
 using namespace isc::util;
 using namespace isc::util;
+using namespace isc::data;
+using namespace isc::config;
+using namespace isc::asiolink;
 
 
 // namespace has to be named, because friends are defined in Dhcpv6Srv class
 // namespace has to be named, because friends are defined in Dhcpv6Srv class
 // Maybe it should be isc::test?
 // Maybe it should be isc::test?
@@ -53,11 +60,14 @@ public:
 
 
 class Dhcpv6SrvTest : public ::testing::Test {
 class Dhcpv6SrvTest : public ::testing::Test {
 public:
 public:
-    // these are empty for now, but let's keep them around
-    Dhcpv6SrvTest() {
+    Dhcpv6SrvTest()
+        : rcode_(-1) {
     }
     }
     ~Dhcpv6SrvTest() {
     ~Dhcpv6SrvTest() {
     };
     };
+
+    int rcode_;
+    ConstElementPtr comment_;
 };
 };
 
 
 TEST_F(Dhcpv6SrvTest, basic) {
 TEST_F(Dhcpv6SrvTest, basic) {
@@ -66,7 +76,7 @@ TEST_F(Dhcpv6SrvTest, basic) {
     // fe80::1234 link-local address on eth0 interface. Obviously
     // fe80::1234 link-local address on eth0 interface. Obviously
     // an attempt to bind this socket will fail.
     // an attempt to bind this socket will fail.
     Dhcpv6Srv* srv = NULL;
     Dhcpv6Srv* srv = NULL;
-    ASSERT_NO_THROW( {
+    ASSERT_NO_THROW({
         // open an unpriviledged port
         // open an unpriviledged port
         srv = new Dhcpv6Srv(DHCP6_SERVER_PORT + 10000);
         srv = new Dhcpv6Srv(DHCP6_SERVER_PORT + 10000);
     });
     });
@@ -78,7 +88,7 @@ TEST_F(Dhcpv6SrvTest, DUID) {
     // tests that DUID is generated properly
     // tests that DUID is generated properly
 
 
     boost::scoped_ptr<Dhcpv6Srv> srv;
     boost::scoped_ptr<Dhcpv6Srv> srv;
-    ASSERT_NO_THROW( {
+    ASSERT_NO_THROW({
         srv.reset(new Dhcpv6Srv(DHCP6_SERVER_PORT + 10000));
         srv.reset(new Dhcpv6Srv(DHCP6_SERVER_PORT + 10000));
     });
     });
 
 
@@ -150,9 +160,39 @@ TEST_F(Dhcpv6SrvTest, DUID) {
     }
     }
 }
 }
 
 
-TEST_F(Dhcpv6SrvTest, Solicit_basic) {
+TEST_F(Dhcpv6SrvTest, solicitBasic) {
+    ConstElementPtr x;
+    string config = "{ \"interface\": [ \"all\" ],"
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet6\": [ { "
+        "    \"pool\": [ \"2001:db8:1234::/80\" ],"
+        "    \"subnet\": \"2001:db8:1234::/64\", "
+        "    \"option-data\": [ {"
+        "          \"name\": \"OPTION_DNS_SERVERS\","
+        "          \"code\": 23,"
+        "          \"data\": \"2001 0DB8 1234 FFFF 0000 0000 0000 0001"
+        "2001 0DB8 1234 FFFF 0000 0000 0000 0002\""
+        "        },"
+        "        {"
+        "          \"name\": \"OPTION_FOO\","
+        "          \"code\": 1000,"
+        "          \"data\": \"1234\""
+        "        } ]"
+        " } ],"
+        "\"valid-lifetime\": 4000 }";
+
+    ElementPtr json = Element::fromJSON(config);
+
     boost::scoped_ptr<NakedDhcpv6Srv> srv;
     boost::scoped_ptr<NakedDhcpv6Srv> srv;
-    ASSERT_NO_THROW( srv.reset(new NakedDhcpv6Srv()) );
+    ASSERT_NO_THROW(srv.reset(new NakedDhcpv6Srv()));
+
+    EXPECT_NO_THROW(x = configureDhcp6Server(*srv, json));
+    ASSERT_TRUE(x);
+    comment_ = parseAnswer(rcode_, x);
+
+    ASSERT_EQ(0, rcode_);
 
 
     // a dummy content for client-id
     // a dummy content for client-id
     OptionBuffer clntDuid(32);
     OptionBuffer clntDuid(32);
@@ -169,10 +209,10 @@ TEST_F(Dhcpv6SrvTest, Solicit_basic) {
     sol->addOption(ia);
     sol->addOption(ia);
 
 
     // Let's not send address in solicit yet
     // Let's not send address in solicit yet
-    // boost::shared_ptr<Option6IAAddr> addr(new Option6IAAddr(D6O_IAADDR,
-    //    IOAddress("2001:db8:1234:ffff::ffff"), 5001, 7001));
-    // ia->addOption(addr);
-    // sol->addOption(ia);
+    /*    boost::shared_ptr<Option6IAAddr>
+        addr(new Option6IAAddr(D6O_IAADDR, IOAddress("2001:db8:1234:ffff::ffff"), 5001, 7001));
+    ia->addOption(addr);
+    sol->addOption(ia); */
 
 
     // constructed very simple SOLICIT message with:
     // constructed very simple SOLICIT message with:
     // - client-id option (mandatory)
     // - client-id option (mandatory)
@@ -191,34 +231,83 @@ TEST_F(Dhcpv6SrvTest, Solicit_basic) {
     boost::shared_ptr<Pkt6> reply = srv->processSolicit(sol);
     boost::shared_ptr<Pkt6> reply = srv->processSolicit(sol);
 
 
     // check if we get response at all
     // check if we get response at all
-    ASSERT_TRUE( reply != boost::shared_ptr<Pkt6>() );
-
-    EXPECT_EQ( DHCPV6_ADVERTISE, reply->getType() );
-    EXPECT_EQ( 1234, reply->getTransid() );
+    ASSERT_TRUE(reply);
+
+    EXPECT_EQ(DHCPV6_ADVERTISE, reply->getType());
+    EXPECT_EQ(1234, reply->getTransid());
+
+    // We have not requested option with code 1000 so it should not
+    // be included in the response.
+    ASSERT_FALSE(reply->getOption(1000));
+
+    // Let's now request option with code 1000.
+    // We expect that server will include this option in its reply.
+    boost::shared_ptr<Option6IntArray<uint16_t> >
+        option_oro(new Option6IntArray<uint16_t>(D6O_ORO));
+    // Create vector with one code equal to 1000.
+    std::vector<uint16_t> codes(1, 1000);
+    // Pass this code to option.
+    option_oro->setValues(codes);
+    // Append ORO to SOLICIT message.
+    sol->addOption(option_oro);
+    
+    // Need to process SOLICIT again after requesting new option.
+    reply = srv->processSolicit(sol);
+    ASSERT_TRUE(reply);
+
+    EXPECT_EQ(DHCPV6_ADVERTISE, reply->getType());
 
 
     OptionPtr tmp = reply->getOption(D6O_IA_NA);
     OptionPtr tmp = reply->getOption(D6O_IA_NA);
-    ASSERT_TRUE( tmp );
+    ASSERT_TRUE(tmp);
 
 
-    Option6IA* reply_ia = dynamic_cast<Option6IA*>(tmp.get());
-    EXPECT_EQ( 234, reply_ia->getIAID() );
+    boost::shared_ptr<Option6IA> reply_ia =
+        boost::dynamic_pointer_cast<Option6IA>(tmp);
+    ASSERT_TRUE(reply_ia);
+    EXPECT_EQ(234, reply_ia->getIAID());
 
 
     // check that there's an address included
     // check that there's an address included
-    EXPECT_TRUE( reply_ia->getOption(D6O_IAADDR));
+    EXPECT_TRUE(reply_ia->getOption(D6O_IAADDR));
 
 
     // check that server included our own client-id
     // check that server included our own client-id
     tmp = reply->getOption(D6O_CLIENTID);
     tmp = reply->getOption(D6O_CLIENTID);
-    ASSERT_TRUE( tmp );
-    EXPECT_EQ(clientid->getType(), tmp->getType() );
-    ASSERT_EQ(clientid->len(), tmp->len() );
+    ASSERT_TRUE(tmp);
+    EXPECT_EQ(clientid->getType(), tmp->getType());
+    ASSERT_EQ(clientid->len(), tmp->len());
 
 
-    EXPECT_TRUE( clientid->getData() == tmp->getData() );
+    EXPECT_TRUE(clientid->getData() == tmp->getData());
 
 
     // check that server included its server-id
     // check that server included its server-id
     tmp = reply->getOption(D6O_SERVERID);
     tmp = reply->getOption(D6O_SERVERID);
-    EXPECT_EQ(tmp->getType(), srv->getServerID()->getType() );
-    ASSERT_EQ(tmp->len(),  srv->getServerID()->len() );
+    EXPECT_EQ(tmp->getType(), srv->getServerID()->getType());
+    ASSERT_EQ(tmp->len(),  srv->getServerID()->len());
 
 
     EXPECT_TRUE(tmp->getData() == srv->getServerID()->getData());
     EXPECT_TRUE(tmp->getData() == srv->getServerID()->getData());
+ 
+    tmp = reply->getOption(D6O_NAME_SERVERS);
+    ASSERT_TRUE(tmp);
+    
+    boost::shared_ptr<Option6AddrLst> reply_nameservers =
+        boost::dynamic_pointer_cast<Option6AddrLst>(tmp);
+    ASSERT_TRUE(reply_nameservers);
+
+    Option6AddrLst::AddressContainer addrs = reply_nameservers->getAddresses();
+    ASSERT_EQ(2, addrs.size());
+    EXPECT_TRUE(addrs[0] == IOAddress("2001:db8:1234:FFFF::1"));
+    EXPECT_TRUE(addrs[1] == IOAddress("2001:db8:1234:FFFF::2"));
+
+    // There is a dummy option with code 1000 we requested from a server.
+    // Expect that this option is in server's response.
+    tmp = reply->getOption(1000);
+    ASSERT_TRUE(tmp);
+
+    // Check that the option contains valid data (from configuration).
+    std::vector<uint8_t> data = tmp->getData();
+    ASSERT_EQ(2, data.size());
+
+    const uint8_t foo_expected[] = {
+        0x12, 0x34
+    };
+    EXPECT_EQ(0, memcmp(&data[0], foo_expected, 2));
 
 
     // more checks to be implemented
     // more checks to be implemented
 }
 }

+ 102 - 0
src/lib/dhcp/libdhcp++.cc

@@ -23,6 +23,8 @@
 #include <dhcp/option.h>
 #include <dhcp/option.h>
 #include <dhcp/option6_ia.h>
 #include <dhcp/option6_ia.h>
 #include <dhcp/option6_iaaddr.h>
 #include <dhcp/option6_iaaddr.h>
+#include <dhcp/option_definition.h>
+#include <dhcp/option6_int_array.h>
 
 
 using namespace std;
 using namespace std;
 using namespace isc::dhcp;
 using namespace isc::dhcp;
@@ -34,6 +36,23 @@ std::map<unsigned short, Option::Factory*> LibDHCP::v4factories_;
 // static array with factories for options
 // static array with factories for options
 std::map<unsigned short, Option::Factory*> LibDHCP::v6factories_;
 std::map<unsigned short, Option::Factory*> LibDHCP::v6factories_;
 
 
+// Static container with DHCPv4 option definitions.
+OptionDefContainer LibDHCP::v4option_defs_;
+
+// Static container with DHCPv6 option definitions.
+OptionDefContainer LibDHCP::v6option_defs_;
+
+const OptionDefContainer&
+LibDHCP::getOptionDefs(Option::Universe u) {
+    switch (u) {
+    case Option::V4:
+        return (v4option_defs_);
+    case Option::V6:
+        return (v6option_defs_);
+    default:
+        isc_throw(isc::BadValue, "invalid universe " << u << " specified");
+    }
+}
 
 
 OptionPtr
 OptionPtr
 LibDHCP::optionFactory(Option::Universe u,
 LibDHCP::optionFactory(Option::Universe u,
@@ -88,6 +107,11 @@ size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
                                               buf.begin() + offset,
                                               buf.begin() + offset,
                                               buf.begin() + offset + opt_len));
                                               buf.begin() + offset + opt_len));
             break;
             break;
+        case D6O_ORO:
+            opt = OptionPtr(new Option6IntArray<uint16_t>(opt_type,
+                                                          buf.begin() + offset,
+                                                          buf.begin() + offset + opt_len));
+            break;
         default:
         default:
             opt = OptionPtr(new Option(Option::V6,
             opt = OptionPtr(new Option(Option::V6,
                                        opt_type,
                                        opt_type,
@@ -201,3 +225,81 @@ void LibDHCP::OptionFactoryRegister(Option::Universe u,
 
 
     return;
     return;
 }
 }
+
+void
+LibDHCP::initStdOptionDefs(Option::Universe u) {
+    switch (u) {
+    case Option::V4:
+        initStdOptionDefs4();
+        break;
+    case Option::V6:
+        initStdOptionDefs6();
+        break;
+    default:
+        isc_throw(isc::BadValue, "invalid universe " << u << " specified");
+    }
+}
+
+void
+LibDHCP::initStdOptionDefs4() {
+    isc_throw(isc::NotImplemented, "initStdOptionDefs4 is not implemented");
+}
+
+void
+LibDHCP::initStdOptionDefs6() {
+    v6option_defs_.clear();
+
+    struct OptionParams {
+        std::string name;
+        uint16_t code;
+        OptionDefinition::DataType type;
+        bool array;
+    };
+    OptionParams params[] = {
+        { "CLIENTID", D6O_CLIENTID, OptionDefinition::BINARY_TYPE, false },
+        { "SERVERID", D6O_SERVERID, OptionDefinition::BINARY_TYPE, false },
+        { "IA_NA", D6O_IA_NA, OptionDefinition::RECORD_TYPE, false },
+        { "IAADDR", D6O_IAADDR, OptionDefinition::RECORD_TYPE, false },
+        { "ORO", D6O_ORO, OptionDefinition::UINT16_TYPE, true },
+        { "ELAPSED_TIME", D6O_ELAPSED_TIME, OptionDefinition::UINT16_TYPE, false },
+        { "STATUS_CODE", D6O_STATUS_CODE, OptionDefinition::RECORD_TYPE, false },
+        { "RAPID_COMMIT", D6O_RAPID_COMMIT, OptionDefinition::EMPTY_TYPE, false },
+        { "DNS_SERVERS", D6O_NAME_SERVERS, OptionDefinition::IPV6_ADDRESS_TYPE, true }
+    };
+    const int params_size = sizeof(params) / sizeof(params[0]);
+
+    for (int i = 0; i < params_size; ++i) {
+        OptionDefinitionPtr definition(new OptionDefinition(params[i].name,
+                                                            params[i].code,
+                                                            params[i].type,
+                                                            params[i].array));
+        switch(params[i].code) {
+        case D6O_IA_NA:
+            for (int j = 0; j < 3; ++j) {
+                definition->addRecordField(OptionDefinition::UINT32_TYPE);
+            }
+            break;
+        case D6O_IAADDR:
+            definition->addRecordField(OptionDefinition::IPV6_ADDRESS_TYPE);
+            definition->addRecordField(OptionDefinition::UINT32_TYPE);
+            definition->addRecordField(OptionDefinition::UINT32_TYPE);
+            break;
+        case D6O_STATUS_CODE:
+            definition->addRecordField(OptionDefinition::UINT16_TYPE);
+            definition->addRecordField(OptionDefinition::STRING_TYPE);
+            break;
+        default:
+            // The default case is intentionally left empty
+            // as it does not need any processing.
+            ;
+        }
+        try {
+            definition->validate();
+        } catch (const Exception& ex) {
+            isc_throw(isc::Unexpected, "internal server error: invalid definition of standard"
+                      << " DHCPv6 option (with code " << params[i].code << "): "
+                      << ex.what());
+        }
+        v6option_defs_.push_back(definition);
+    }
+}

+ 53 - 4
src/lib/dhcp/libdhcp++.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2012  Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
 // purpose with or without fee is hereby granted, provided that the above
@@ -15,9 +15,10 @@
 #ifndef LIBDHCP_H_
 #ifndef LIBDHCP_H_
 #define LIBDHCP_H_
 #define LIBDHCP_H_
 
 
-#include <iostream>
-#include <util/buffer.h>
+#include <dhcp/option_definition.h>
 #include <dhcp/pkt6.h>
 #include <dhcp/pkt6.h>
+#include <util/buffer.h>
+#include <iostream>
 
 
 namespace isc {
 namespace isc {
 namespace dhcp {
 namespace dhcp {
@@ -29,6 +30,12 @@ public:
     /// Map of factory functions.
     /// Map of factory functions.
     typedef std::map<unsigned short, Option::Factory*>  FactoryMap;
     typedef std::map<unsigned short, Option::Factory*>  FactoryMap;
 
 
+    /// @brief Return collection of option definitions.
+    ///
+    /// @param u universe of the options (V4 or V6).
+    /// @return collection of option definitions.
+    static const OptionDefContainer& getOptionDefs(Option::Universe u);
+
     /// @brief Factory function to create instance of option.
     /// @brief Factory function to create instance of option.
     ///
     ///
     /// Factory method creates instance of specified option. The option
     /// Factory method creates instance of specified option. The option
@@ -102,12 +109,54 @@ public:
     static void OptionFactoryRegister(Option::Universe u,
     static void OptionFactoryRegister(Option::Universe u,
                                       uint16_t type,
                                       uint16_t type,
                                       Option::Factory * factory);
                                       Option::Factory * factory);
-protected:
+
+    /// Initialize standard DHCP options (V4 or V6).
+    ///
+    /// The method creates option definitions for all options
+    /// (DHCPv4 or DHCPv6 depending on universe specified).
+    /// Currently DHCPv4 option definitions initialization is not
+    /// implemented thus this function will throw isc::NotImplemented
+    /// if V4 universe is specified.
+    ///
+    /// @param u universe
+    /// @throw isc::Unexpected if internal error occured during option
+    /// definitions creation.
+    /// @throw std::bad_alloc if system went out of memory.
+    /// @throw isc::NotImplemented when V4 universe specified.
+    static void initStdOptionDefs(Option::Universe u);
+
+private:
+
+    /// Initialize standard DHCPv4 option definitions.
+    ///
+    /// The method creates option definitions for all DHCPv4 options.
+    /// Currently this function is not implemented.
+    ///
+    /// @todo implemend this function.
+    ///
+    /// @throw isc::NotImplemeneted
+    static void initStdOptionDefs4();
+
+    /// Initialize standard DHCPv6 option definitions.
+    ///
+    /// The method creates option definitions for all DHCPv6 options.
+    ///
+    /// @throw isc::Unexpected if internal error occured during option
+    /// definitions creation.
+    /// @throw std::bad_alloc if system went out of memory.
+    static void initStdOptionDefs6();
+
     /// pointers to factories that produce DHCPv6 options
     /// pointers to factories that produce DHCPv6 options
     static FactoryMap v4factories_;
     static FactoryMap v4factories_;
 
 
     /// pointers to factories that produce DHCPv6 options
     /// pointers to factories that produce DHCPv6 options
     static FactoryMap v6factories_;
     static FactoryMap v6factories_;
+
+    /// Container with DHCPv4 option definitions.
+    static OptionDefContainer v4option_defs_;
+
+    /// Container with DHCPv6 option definitions.
+    static OptionDefContainer v6option_defs_;
 };
 };
 
 
 }
 }

+ 6 - 1
src/lib/dhcp/option_definition.cc

@@ -29,6 +29,7 @@ namespace dhcp {
 
 
 OptionDefinition::DataTypeUtil::DataTypeUtil() {
 OptionDefinition::DataTypeUtil::DataTypeUtil() {
     data_types_["empty"] = EMPTY_TYPE;
     data_types_["empty"] = EMPTY_TYPE;
+    data_types_["binary"] = BINARY_TYPE;
     data_types_["boolean"] = BOOLEAN_TYPE;
     data_types_["boolean"] = BOOLEAN_TYPE;
     data_types_["int8"] = INT8_TYPE;
     data_types_["int8"] = INT8_TYPE;
     data_types_["int16"] = INT16_TYPE;
     data_types_["int16"] = INT16_TYPE;
@@ -97,11 +98,15 @@ OptionDefinition::addRecordField(const DataType data_type) {
 
 
 Option::Factory*
 Option::Factory*
 OptionDefinition::getFactory() const {
 OptionDefinition::getFactory() const {
+    validate();
+
     // @todo This function must be extended to return more factory
     // @todo This function must be extended to return more factory
     // functions that create instances of more specialized options.
     // functions that create instances of more specialized options.
     // This requires us to first implement those more specialized
     // This requires us to first implement those more specialized
     // options that will be derived from Option class.
     // options that will be derived from Option class.
-    if (type_ == IPV6_ADDRESS_TYPE && array_type_) {
+    if (type_ == BINARY_TYPE) {
+        return (factoryGeneric);
+    } else if (type_ == IPV6_ADDRESS_TYPE && array_type_) {
         return (factoryAddrList6);
         return (factoryAddrList6);
     } else if (type_ == IPV4_ADDRESS_TYPE && array_type_) {
     } else if (type_ == IPV4_ADDRESS_TYPE && array_type_) {
         return (factoryAddrList4);
         return (factoryAddrList4);

+ 85 - 4
src/lib/dhcp/option_definition.h

@@ -16,13 +16,42 @@
 #define OPTION_DEFINITION_H_
 #define OPTION_DEFINITION_H_
 
 
 #include <dhcp/option_data_types.h>
 #include <dhcp/option_data_types.h>
-#include <dhcp/option6_int.h>
-#include <dhcp/option6_int_array.h>
 #include <dhcp/option.h>
 #include <dhcp/option.h>
+#include <boost/shared_ptr.hpp>
+#include <boost/multi_index_container.hpp>
+#include <boost/multi_index/hashed_index.hpp>
+#include <boost/multi_index/sequenced_index.hpp>
+#include <boost/multi_index/mem_fun.hpp>
 
 
 namespace isc {
 namespace isc {
 namespace dhcp {
 namespace dhcp {
 
 
+/// @brief Forward declaration to OptionDefinition.
+class OptionDefinition;
+
+/// @brief Pointer to option definition object.
+typedef boost::shared_ptr<OptionDefinition> OptionDefinitionPtr;
+
+/// @brief Forward declaration to Option6Int.
+///
+/// This forward declaration is needed to access Option6Int class
+/// without having to include option6_int.h header. This is because
+/// this header includes libdhcp++.h and this causes circular
+/// inclusion between libdhcp++.h, option_definition.h and
+/// option6_int.h.
+template<typename T>
+class Option6Int;
+
+/// @brief Forward declaration to Option6IntArray.
+///
+/// This forward declaration is needed to access Option6IntArray class
+/// without having to include option6_int_array.h header. This is because
+/// this header includes libdhcp++.h and this causes circular
+/// inclusion between libdhcp++.h, option_definition.h and
+/// option6_int_array.h.
+template<typename T>
+class Option6IntArray;
+
 /// @brief Base class representing a DHCP option definition.
 /// @brief Base class representing a DHCP option definition.
 ///
 ///
 /// This is a base class representing a DHCP option definition, which describes
 /// This is a base class representing a DHCP option definition, which describes
@@ -52,7 +81,7 @@ namespace dhcp {
 ///
 ///
 /// Should the option comprise data fields of different types, the "record"
 /// Should the option comprise data fields of different types, the "record"
 /// option type is used. In such cases the data field types within the record
 /// option type is used. In such cases the data field types within the record
-/// are specified using \ref OptionDefinition::addRecordField.
+/// are specified using \ref OptioDefinition::addRecordField.
 ///
 ///
 /// When the OptionDefinition object has been sucessfully created, it can be
 /// When the OptionDefinition object has been sucessfully created, it can be
 /// queried to return the appropriate option factory function for the specified
 /// queried to return the appropriate option factory function for the specified
@@ -84,6 +113,7 @@ public:
     /// Data types of DHCP option fields.
     /// Data types of DHCP option fields.
     enum DataType {
     enum DataType {
         EMPTY_TYPE,
         EMPTY_TYPE,
+        BINARY_TYPE,
         BOOLEAN_TYPE,
         BOOLEAN_TYPE,
         INT8_TYPE,
         INT8_TYPE,
         INT16_TYPE,
         INT16_TYPE,
@@ -202,7 +232,9 @@ public:
 
 
     /// @brief Return factory function for the given definition.
     /// @brief Return factory function for the given definition.
     ///
     ///
-    /// @return pointer to factory function.
+    /// @throw isc::OutOfRange if \ref validate returns it.
+    /// @throw isc::BadValue if \ref validate returns it.
+    /// @return pointer to a factory function.
     Option::Factory* getFactory() const;
     Option::Factory* getFactory() const;
 
 
     /// @brief Return option name.
     /// @brief Return option name.
@@ -377,6 +409,55 @@ private:
 };
 };
 
 
 
 
+/// @brief Multi index container for DHCP option definitions.
+///
+/// This container allows to search for DHCP option definition
+/// using two indexes:
+/// - sequenced: used to access elements in the order they have
+/// been added to the container
+/// - option code: used to search defintions of options
+/// with a specified option code (aka option type).
+/// Note that this container can hold multiple options with the
+/// same code. For this reason, the latter index can be used to
+/// obtain a range of options for a particular option code.
+/// 
+/// @todo: need an index to search options using option space name
+/// once option spaces are implemented.
+typedef boost::multi_index_container<
+    // Container comprises elements of OptionDefinition type.
+    OptionDefinitionPtr,
+    // Here we start enumerating various indexes.
+    boost::multi_index::indexed_by<
+        // Sequenced index allows accessing elements in the same way
+        // as elements in std::list. Sequenced is an index #0.
+        boost::multi_index::sequenced<>,
+        // Start definition of index #1.
+        boost::multi_index::hashed_non_unique<
+            // Use option type as the index key. The type is held
+            // in OptionDefinition object so we have to call
+            // OptionDefinition::getCode to retrieve this key
+            // for each element. The option code is non-unique so
+            // multiple elements with the same option code can
+            // be returned by this index.
+            boost::multi_index::const_mem_fun<
+                OptionDefinition,
+                uint16_t,
+                &OptionDefinition::getCode
+            >
+        >
+    >
+> OptionDefContainer;
+
+/// Type of the index #1 - option type.
+typedef OptionDefContainer::nth_index<1>::type OptionDefContainerTypeIndex;
+/// Pair of iterators to represent the range of options definitions
+///  having the same option type value. The first element in this pair
+///  represents the begining of the range, the second element
+///  represents the end.
+typedef std::pair<OptionDefContainerTypeIndex::const_iterator,
+                  OptionDefContainerTypeIndex::const_iterator> OptionDefContainerTypeRange;
+
+
 } // namespace isc::dhcp
 } // namespace isc::dhcp
 } // namespace isc
 } // namespace isc
 
 

+ 82 - 0
src/lib/dhcp/tests/libdhcp++_unittest.cc

@@ -21,6 +21,11 @@
 #include <dhcp/dhcp4.h>
 #include <dhcp/dhcp4.h>
 #include <dhcp/dhcp6.h>
 #include <dhcp/dhcp6.h>
 #include <dhcp/libdhcp++.h>
 #include <dhcp/libdhcp++.h>
+#include <dhcp/option6_ia.h>
+#include <dhcp/option6_iaaddr.h>
+#include <dhcp/option6_int.h>
+#include <dhcp/option6_int_array.h>
+#include <dhcp/option6_addrlst.h>
 #include "config.h"
 #include "config.h"
 
 
 using namespace std;
 using namespace std;
@@ -46,6 +51,57 @@ public:
         Option* option = new Option(u, type, buf);
         Option* option = new Option(u, type, buf);
         return OptionPtr(option);
         return OptionPtr(option);
     }
     }
+
+    /// @brief Test option option definition.
+    ///
+    /// This function tests if option definition for standard
+    /// option has been initialized correctly.
+    ///
+    /// @param code option code.
+    /// @param bug buffer to be used to create option instance.
+    /// @param expected_type type of the option created by the
+    /// factory function returned by the option definition.
+    static void testInitOptionDefs6(const uint16_t code,
+                             const OptionBuffer& buf,
+                             const std::type_info& expected_type) {
+        // Initialize stdandard options definitions. They are held
+        // in the static container throughout the program.
+        LibDHCP::initStdOptionDefs(Option::V6);
+        // Get all option definitions, we will use them to extract
+        // the definition for a particular option code.
+        OptionDefContainer options = LibDHCP::getOptionDefs(Option::V6);
+        // Get the container index #1. This one allows for searching
+        // option definitions using option code.
+        const OptionDefContainerTypeIndex& idx = options.get<1>();
+        // Get 'all' option definitions for a particular option code.
+        // For standard options we expect that the range returned
+        // will contain single option as their codes are unique.
+        OptionDefContainerTypeRange range = idx.equal_range(code);
+        ASSERT_EQ(1, std::distance(range.first, range.second));
+        // If we have single option definition returned, the
+        // first iterator holds it.
+        OptionDefinitionPtr def = *(range.first);
+        // It should not happen that option definition is NULL but
+        // let's make sure (test should take things like that into
+        // account).
+        ASSERT_TRUE(def);
+        // Check that option definition is valid.
+        ASSERT_NO_THROW(def->validate());
+        // Get the factory function for the particular option
+        // definition. We will use this factory function to
+        // create option instance.
+        Option::Factory* factory = NULL;
+        ASSERT_NO_THROW(factory = def->getFactory());
+        OptionPtr option;
+        // Create the option.
+        ASSERT_NO_THROW(option = factory(Option::V6, code, buf));
+        // Make sure it is not NULL.
+        ASSERT_TRUE(option);
+        // And the actual object type is the one that we expect.
+        // Note that for many options there are dedicated classes
+        // derived from Option class to represent them.
+        EXPECT_TRUE(typeid(*option) == expected_type);
+    }
 };
 };
 
 
 static const uint8_t packed[] = {
 static const uint8_t packed[] = {
@@ -311,4 +367,30 @@ TEST(LibDhcpTest, unpackOptions4) {
     EXPECT_TRUE(x == options.end()); // option 2 not found
     EXPECT_TRUE(x == options.end()); // option 2 not found
 }
 }
 
 
+// Test that definitions of standard options have been initialized
+// correctly.
+// @todo Only limited number of option definitions are now created
+// This test have to be extended once all option definitions are
+// created.
+TEST(LibDhcpTest, initStdOptionDefs) {
+    LibDhcpTest::testInitOptionDefs6(D6O_CLIENTID, OptionBuffer(14, 1),
+                                     typeid(Option));
+    LibDhcpTest::testInitOptionDefs6(D6O_SERVERID, OptionBuffer(14, 1),
+                                     typeid(Option));
+    LibDhcpTest::testInitOptionDefs6(D6O_IA_NA, OptionBuffer(12, 1),
+                                     typeid(Option6IA));
+    LibDhcpTest::testInitOptionDefs6(D6O_IAADDR, OptionBuffer(24, 1),
+                                     typeid(Option6IAAddr));
+    LibDhcpTest::testInitOptionDefs6(D6O_ORO, OptionBuffer(10, 1),
+                                     typeid(Option6IntArray<uint16_t>));
+    LibDhcpTest::testInitOptionDefs6(D6O_ELAPSED_TIME, OptionBuffer(2, 1),
+                                     typeid(Option6Int<uint16_t>));
+    LibDhcpTest::testInitOptionDefs6(D6O_STATUS_CODE, OptionBuffer(10, 1),
+                                     typeid(Option));
+    LibDhcpTest::testInitOptionDefs6(D6O_RAPID_COMMIT, OptionBuffer(),
+                                     typeid(Option));
+    LibDhcpTest::testInitOptionDefs6(D6O_NAME_SERVERS, OptionBuffer(32, 1),
+                                     typeid(Option6AddrLst));
+}
+
 }
 }

+ 50 - 0
src/lib/dhcp/tests/option_definition_unittest.cc

@@ -281,6 +281,56 @@ TEST_F(OptionDefinitionTest, factoryEmpty) {
     EXPECT_THROW(factory(Option::V6, D6O_RAPID_COMMIT,OptionBuffer(2)),isc::BadValue);
     EXPECT_THROW(factory(Option::V6, D6O_RAPID_COMMIT,OptionBuffer(2)),isc::BadValue);
 }
 }
 
 
+TEST_F(OptionDefinitionTest, factoryBinary) {
+    // Binary option is the one that is represented by the generic
+    // Option class. In fact all options can be represented by this
+    // class but for some of them it is just natural. The SERVERID
+    // option consists of the option code, length and binary data so
+    // this one was picked for this test.
+    OptionDefinition opt_def("OPTION_SERVERID", D6O_SERVERID, "binary");
+    Option::Factory* factory(NULL);
+    EXPECT_NO_THROW(factory = opt_def.getFactory());
+    ASSERT_TRUE(factory != NULL);
+
+    // Prepare some dummy data (serverid): 0, 1, 2 etc.
+    OptionBuffer buf(14);
+    for (int i = 0; i < 14; ++i) {
+        buf[i] = i;
+    }
+    // Create option instance with the factory function.
+    // If the OptionDefinition code works properly than
+    // object of the type Option should be returned.
+    OptionPtr option_v6;
+    ASSERT_NO_THROW(
+        option_v6 = factory(Option::V6, D6O_SERVERID, buf);
+    );
+    // Expect base option type returned.
+    ASSERT_TRUE(typeid(*option_v6) == typeid(Option));
+    // Sanity check on universe, length and size. These are
+    // the basic parameters identifying any option.
+    EXPECT_EQ(Option::V6, option_v6->getUniverse());
+    EXPECT_EQ(4, option_v6->getHeaderLen());
+    ASSERT_EQ(buf.size(), option_v6->getData().size());
+
+    // Get the server id data from the option and compare
+    // against reference buffer. They are expected to match.
+    EXPECT_TRUE(std::equal(option_v6->getData().begin(),
+                           option_v6->getData().end(),
+                           buf.begin()));
+
+    // Repeat the same test scenario for DHCPv4 option.
+    OptionPtr option_v4;
+    ASSERT_NO_THROW(option_v4 = factory(Option::V4, 214, buf));
+    // Expect 'empty' DHCPv4 option.
+    EXPECT_EQ(Option::V4, option_v4->getUniverse());
+    EXPECT_EQ(2, option_v4->getHeaderLen());
+    ASSERT_EQ(buf.size(), option_v4->getData().size());
+
+    EXPECT_TRUE(std::equal(option_v6->getData().begin(),
+                           option_v6->getData().end(),
+                           buf.begin()));
+}
+
 TEST_F(OptionDefinitionTest, factoryIA6) {
 TEST_F(OptionDefinitionTest, factoryIA6) {
     // This option consists of IAID, T1 and T2 fields (each 4 bytes long).
     // This option consists of IAID, T1 and T2 fields (each 4 bytes long).
     const int option6_ia_len = 12;
     const int option6_ia_len = 12;