Browse Source

[master] Merge branch 'trac4498'

Marcin Siodelski 8 years ago
parent
commit
14716853a9

+ 0 - 105
src/bin/dhcp4/dhcp4_srv.cc

@@ -654,15 +654,6 @@ Dhcpv4Srv::processPacket(Pkt4Ptr& query, Pkt4Ptr& rsp) {
     isc::stats::StatsMgr::instance().addValue("pkt4-received",
     isc::stats::StatsMgr::instance().addValue("pkt4-received",
                                               static_cast<int64_t>(1));
                                               static_cast<int64_t>(1));
 
 
-    // In order to parse the DHCP options, the server needs to use some
-    // configuration information such as: existing option spaces, option
-    // definitions etc. This is the kind of information which is not
-    // available in the libdhcp, so we need to supply our own implementation
-    // of the option parsing function here, which would rely on the
-    // configuration data.
-    query->setCallback(boost::bind(&Dhcpv4Srv::unpackOptions, this,
-                                   _1, _2, _3));
-
     bool skip_unpack = false;
     bool skip_unpack = false;
 
 
     // The packet has just been received so contains the uninterpreted wire
     // The packet has just been received so contains the uninterpreted wire
@@ -2355,102 +2346,6 @@ Dhcpv4Srv::sanityCheck(const Pkt4Ptr& query, RequirementLevel serverid) {
     }
     }
 }
 }
 
 
-size_t
-Dhcpv4Srv::unpackOptions(const OptionBuffer& buf,
-                         const std::string& option_space,
-                         isc::dhcp::OptionCollection& options) {
-    size_t offset = 0;
-
-    OptionDefContainer option_defs;
-    if (option_space == "dhcp4") {
-        // Get the list of standard option definitions.
-        option_defs = LibDHCP::getOptionDefs(Option::V4);
-    } else if (!option_space.empty()) {
-        OptionDefContainerPtr option_defs_ptr = CfgMgr::instance()
-            .getCurrentCfg()->getCfgOptionDef()->getAll(option_space);
-        if (option_defs_ptr != NULL) {
-            option_defs = *option_defs_ptr;
-        }
-    }
-    // Get the search index #1. It allows to search for option definitions
-    // using option code.
-    const OptionDefContainerTypeIndex& idx = option_defs.get<1>();
-
-    // The buffer being read comprises a set of options, each starting with
-    // a one-byte type code and a one-byte length field.
-    while (offset + 1 <= buf.size()) {
-        uint8_t opt_type = buf[offset++];
-
-        // DHO_END is a special, one octet long option
-        if (opt_type == DHO_END)
-            return (offset); // just return. Don't need to add DHO_END option
-
-        // DHO_PAD is just a padding after DHO_END. Let's continue parsing
-        // in case we receive a message without DHO_END.
-        if (opt_type == DHO_PAD)
-            continue;
-
-        if (offset + 1 > buf.size()) {
-            // opt_type must be cast to integer so as it is not treated as
-            // unsigned char value (a number is presented in error message).
-            isc_throw(OutOfRange, "Attempt to parse truncated option "
-                      << static_cast<int>(opt_type));
-        }
-
-        uint8_t opt_len =  buf[offset++];
-        if (offset + opt_len > buf.size()) {
-
-            // We peeked at the option header of the next option, but discovered
-            // that it would end up beyond buffer end, so the option is
-            // truncated. Hence we can't parse it. Therefore we revert
-            // back by two bytes (as if we never parsed them).
-            return (offset - 2);
-
-            // isc_throw(OutOfRange, "Option parse failed. Tried to parse "
-            //          << offset + opt_len << " bytes from " << buf.size()
-            //          << "-byte long buffer.");
-        }
-
-        // Get all definitions with the particular option code. Note that option code
-        // is non-unique within this container however at this point we expect
-        // to get one option definition with the particular code. If more are
-        // returned we report an error.
-        const OptionDefContainerTypeRange& range = idx.equal_range(opt_type);
-        // Get the number of returned option definitions for the option code.
-        size_t num_defs = distance(range.first, range.second);
-
-        OptionPtr opt;
-        if (num_defs > 1) {
-            // Multiple options of the same code are not supported right now!
-            isc_throw(isc::Unexpected, "Internal error: multiple option definitions"
-                      " for option type " << static_cast<int>(opt_type)
-                      << " returned. Currently it is not supported to initialize"
-                      << " multiple option definitions for the same option code."
-                      << " This will be supported once support for option spaces"
-                      << " is implemented");
-        } else if (num_defs == 0) {
-            opt = OptionPtr(new Option(Option::V4, opt_type,
-                                       buf.begin() + offset,
-                                       buf.begin() + offset + opt_len));
-            opt->setEncapsulatedSpace("dhcp4");
-        } else {
-            // The option definition has been found. Use it to create
-            // the option instance from the provided buffer chunk.
-            const OptionDefinitionPtr& def = *(range.first);
-            assert(def);
-            opt = def->optionFactory(Option::V4, opt_type,
-                                     buf.begin() + offset,
-                                     buf.begin() + offset + opt_len,
-                                     boost::bind(&Dhcpv4Srv::unpackOptions,
-                                                 this, _1, _2, _3));
-        }
-
-        options.insert(std::make_pair(opt_type, opt));
-        offset += opt_len;
-    }
-    return (offset);
-}
-
 void Dhcpv4Srv::classifyByVendor(const Pkt4Ptr& pkt, std::string& classes) {
 void Dhcpv4Srv::classifyByVendor(const Pkt4Ptr& pkt, std::string& classes) {
     // Built-in vendor class processing
     // Built-in vendor class processing
     boost::shared_ptr<OptionString> vendor_class =
     boost::shared_ptr<OptionString> vendor_class =

+ 0 - 12
src/bin/dhcp4/dhcp4_srv.h

@@ -729,18 +729,6 @@ protected:
     /// simulates transmission of a packet. For that purpose it is protected.
     /// simulates transmission of a packet. For that purpose it is protected.
     virtual void sendPacket(const Pkt4Ptr& pkt);
     virtual void sendPacket(const Pkt4Ptr& pkt);
 
 
-    /// @brief Implements a callback function to parse options in the message.
-    ///
-    /// @param buf a A buffer holding options in on-wire format.
-    /// @param option_space A name of the option space which holds definitions
-    /// of to be used to parse options in the packets.
-    /// @param [out] options A reference to the collection where parsed options
-    /// will be stored.
-    /// @return An offset to the first byte after last parsed option.
-    size_t unpackOptions(const OptionBuffer& buf,
-                         const std::string& option_space,
-                         isc::dhcp::OptionCollection& options);
-
     /// @brief Assigns incoming packet to zero or more classes.
     /// @brief Assigns incoming packet to zero or more classes.
     ///
     ///
     /// @note This is done in two phases: first the content of the
     /// @note This is done in two phases: first the content of the

+ 2 - 182
src/bin/dhcp4/tests/dhcp4_srv_unittest.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2011-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -15,6 +15,7 @@
 #include <dhcp/tests/pkt_captures.h>
 #include <dhcp/tests/pkt_captures.h>
 #include <dhcp/dhcp4.h>
 #include <dhcp/dhcp4.h>
 #include <dhcp/iface_mgr.h>
 #include <dhcp/iface_mgr.h>
+#include <dhcp/libdhcp++.h>
 #include <dhcp/option.h>
 #include <dhcp/option.h>
 #include <dhcp/option_int.h>
 #include <dhcp/option_int.h>
 #include <dhcp/option4_addrlst.h>
 #include <dhcp/option4_addrlst.h>
@@ -1142,187 +1143,6 @@ TEST_F(Dhcpv4SrvTest, vendorOptionsDocsis) {
 /// See ticket #3057
 /// See ticket #3057
 
 
 
 
-// This test verifies that the following option structure can be parsed:
-// - option (option space 'foobar')
-//   - sub option (option space 'foo')
-//      - sub option (option space 'bar')
-// @todo Add more thorough unit tests for unpackOptions.
-TEST_F(Dhcpv4SrvTest, unpackSubOptions) {
-    // Create option definition for each level of encapsulation. Each option
-    // definition is for the option code 1. Options may have the same
-    // option code because they belong to different option spaces.
-
-    // Top level option encapsulates options which belong to 'space-foo'.
-    OptionDefinitionPtr opt_def(new OptionDefinition("option-foobar", 1, "uint32",
-                                                      "space-foo"));\
-    // Middle option encapsulates options which belong to 'space-bar'
-    OptionDefinitionPtr opt_def2(new OptionDefinition("option-foo", 1, "uint16",
-                                                      "space-bar"));
-    // Low level option doesn't encapsulate any option space.
-    OptionDefinitionPtr opt_def3(new OptionDefinition("option-bar", 1,
-                                                      "uint8"));
-
-    // Add option definitions to the Configuration Manager. Each goes under
-    // different option space.
-    CfgOptionDefPtr cfg_option_def =
-        CfgMgr::instance().getStagingCfg()->getCfgOptionDef();
-    ASSERT_NO_THROW(cfg_option_def->add(opt_def, "space-foobar"));
-    ASSERT_NO_THROW(cfg_option_def->add(opt_def2, "space-foo"));
-    ASSERT_NO_THROW(cfg_option_def->add(opt_def3, "space-bar"));
-    CfgMgr::instance().commit();
-
-    // Create the buffer holding the structure of options.
-    const uint8_t raw_data[] = {
-        // First option starts here.
-        0x01,                   // option code = 1
-        0x0B,                   // option length = 11
-        0x00, 0x01, 0x02, 0x03, // This option carries uint32 value
-        // Sub option starts here.
-        0x01,                   // option code = 1
-        0x05,                   // option length = 5
-        0x01, 0x02,             // this option carries uint16 value
-        // Last option starts here.
-        0x01,                   // option code = 1
-        0x01,                   // option length = 1
-        0x00                    // This option carries a single uint8
-                                // value and has no sub options.
-    };
-    size_t raw_data_len = sizeof(raw_data) / sizeof(uint8_t);
-    OptionBuffer buf(raw_data, raw_data + raw_data_len);
-
-    // Parse options.
-    NakedDhcpv4Srv srv(0);
-    OptionCollection options;
-    ASSERT_NO_THROW(srv.unpackOptions(buf, "space-foobar", options));
-
-    // There should be one top level option.
-    ASSERT_EQ(1, options.size());
-    boost::shared_ptr<OptionInt<uint32_t> > option_foobar =
-        boost::dynamic_pointer_cast<OptionInt<uint32_t> >(options.begin()->
-                                                          second);
-    ASSERT_TRUE(option_foobar);
-    EXPECT_EQ(1, option_foobar->getType());
-    EXPECT_EQ(0x00010203, option_foobar->getValue());
-    // There should be a middle level option held in option_foobar.
-    boost::shared_ptr<OptionInt<uint16_t> > option_foo =
-        boost::dynamic_pointer_cast<OptionInt<uint16_t> >(option_foobar->
-                                                          getOption(1));
-    ASSERT_TRUE(option_foo);
-    EXPECT_EQ(1, option_foo->getType());
-    EXPECT_EQ(0x0102, option_foo->getValue());
-    // Finally, there should be a low level option under option_foo.
-    boost::shared_ptr<OptionInt<uint8_t> > option_bar =
-        boost::dynamic_pointer_cast<OptionInt<uint8_t> >(option_foo->getOption(1));
-    ASSERT_TRUE(option_bar);
-    EXPECT_EQ(1, option_bar->getType());
-    EXPECT_EQ(0x0, option_bar->getValue());
-}
-
-// Check parsing of an empty option
-TEST_F(Dhcpv4SrvTest, unpackEmptyOption) {
-    // Create option definition for the option code 1 without fields.
-    OptionDefinitionPtr opt_def(new OptionDefinition("option-empty", 1,
-                                                     "empty", false));
-
-    // Add it to the Configuration Manager.
-    CfgOptionDefPtr cfg_option_def =
-        CfgMgr::instance().getStagingCfg()->getCfgOptionDef();
-    ASSERT_NO_THROW(cfg_option_def->add(opt_def, "space-empty"));
-    CfgMgr::instance().commit();
-
-    // Create the buffer holding the structure of the empty option.
-    const uint8_t raw_data[] = {
-      0x01,                     // option code = 1
-      0x00                      // option length = 0
-    };
-    size_t raw_data_len = sizeof(raw_data) / sizeof(uint8_t);
-    OptionBuffer buf(raw_data, raw_data + raw_data_len);
-
-    // Parse options.
-    NakedDhcpv4Srv srv(0);
-    OptionCollection options;
-    ASSERT_NO_THROW(srv.unpackOptions(buf, "space-empty", options));
-
-    // There should be one option.
-    ASSERT_EQ(1, options.size());
-    OptionPtr option_empty = options.begin()->second;
-    ASSERT_TRUE(option_empty);
-    EXPECT_EQ(1, option_empty->getType());
-    EXPECT_EQ(2, option_empty->len());
-}
-
-// Check parsing of an empty VSI sub option
-TEST_F(Dhcpv4SrvTest, unpackVSIOption) {
-    // Create the buffer holding the structure of the Vendor-Specific Info
-    const uint8_t raw_data[] = {
-      43,     // option code = DHO_VENDOR_ENCAPSULATED_OPTIONS
-      2,      // option length
-      0xdc,   // suboption code
-      0       // suboption length
-    };
-    size_t raw_data_len = sizeof(raw_data) / sizeof(uint8_t);
-    OptionBuffer buf(raw_data, raw_data + raw_data_len);
-
-    // Parse options.
-    NakedDhcpv4Srv srv(0);
-    OptionCollection options;
-    ASSERT_NO_THROW(srv.unpackOptions(buf, "dhcp4", options));
-
-    // There should be one option: the VSI
-    ASSERT_EQ(1, options.size());
-    OptionPtr vsi = options.begin()->second;
-    ASSERT_TRUE(vsi);
-    EXPECT_EQ(DHO_VENDOR_ENCAPSULATED_OPTIONS, vsi->getType());
-    OptionCollection suboptions = vsi->getOptions();
-
-    // There should be one suboption
-    ASSERT_EQ(1, suboptions.size());
-    OptionPtr eso = suboptions.begin()->second;
-    ASSERT_TRUE(eso);
-    EXPECT_EQ(0xdc, eso->getType());
-    EXPECT_EQ(2, eso->len());
-}
-
-// Check parsing of an empty VIVSI sub option
-TEST_F(Dhcpv4SrvTest, unpackVIVSIOption) {
-    // Create the buffer holding the structure of the Vendor-Identifying
-    // Vendor-Specific Info
-    const uint8_t raw_data[] = {
-      125,            // option code = DHO_VIVSO_SUBOPTIONS
-      7,              // option length
-      0, 0, 9, 0xbf,  // ISC enterprise number (2495)
-      2,              // option data length
-      0xdc,           // suboption code
-      0               // suboption length
-    };
-    size_t raw_data_len = sizeof(raw_data) / sizeof(uint8_t);
-    OptionBuffer buf(raw_data, raw_data + raw_data_len);
-
-    // Parse options.
-    NakedDhcpv4Srv srv(0);
-    OptionCollection options;
-    ASSERT_NO_THROW(srv.unpackOptions(buf, "dhcp4", options));
-
-    // There should be one option: the VIVSI
-    ASSERT_EQ(1, options.size());
-    OptionPtr vivsi = options.begin()->second;
-    ASSERT_TRUE(vivsi);
-    EXPECT_EQ(DHO_VIVSO_SUBOPTIONS, vivsi->getType());
-
-    // Cast to OptionVendor
-    OptionVendorPtr vsi = boost::dynamic_pointer_cast<OptionVendor>(vivsi);
-    ASSERT_TRUE(vsi);
-    EXPECT_EQ(2495, vsi->getVendorId());
-    OptionCollection suboptions = vsi->getOptions();
-
-    // There should be one suboption
-    ASSERT_EQ(1, suboptions.size());
-    OptionPtr eso = suboptions.begin()->second;
-    ASSERT_TRUE(eso);
-    EXPECT_EQ(0xdc, eso->getType());
-    EXPECT_EQ(2, eso->len());
-}
-
 // Checks whether the server uses default (0.0.0.0) siaddr value, unless
 // Checks whether the server uses default (0.0.0.0) siaddr value, unless
 // explicitly specified
 // explicitly specified
 TEST_F(Dhcpv4SrvTest, siaddrDefault) {
 TEST_F(Dhcpv4SrvTest, siaddrDefault) {

+ 5 - 0
src/bin/dhcp4/tests/dhcp4_test_utils.cc

@@ -11,6 +11,7 @@
 #include <cc/command_interpreter.h>
 #include <cc/command_interpreter.h>
 #include <dhcp4/json_config_parser.h>
 #include <dhcp4/json_config_parser.h>
 #include <dhcp4/tests/dhcp4_test_utils.h>
 #include <dhcp4/tests/dhcp4_test_utils.h>
+#include <dhcp/libdhcp++.h>
 #include <dhcp/option4_addrlst.h>
 #include <dhcp/option4_addrlst.h>
 #include <dhcp/option_int.h>
 #include <dhcp/option_int.h>
 #include <dhcp/option_int_array.h>
 #include <dhcp/option_int_array.h>
@@ -67,6 +68,8 @@ Dhcpv4SrvTest::Dhcpv4SrvTest()
     CfgMgr::instance().getStagingCfg()->getCfgSubnets4()->add(subnet_);
     CfgMgr::instance().getStagingCfg()->getCfgSubnets4()->add(subnet_);
     CfgMgr::instance().commit();
     CfgMgr::instance().commit();
 
 
+    LibDHCP::clearRuntimeOptionDefs();
+
     // Let's wipe all existing statistics.
     // Let's wipe all existing statistics.
     isc::stats::StatsMgr::instance().removeAll();
     isc::stats::StatsMgr::instance().removeAll();
 }
 }
@@ -77,6 +80,8 @@ Dhcpv4SrvTest::~Dhcpv4SrvTest() {
     CfgMgr::instance().clear();
     CfgMgr::instance().clear();
     CfgMgr::instance().echoClientId(true);
     CfgMgr::instance().echoClientId(true);
 
 
+    LibDHCP::clearRuntimeOptionDefs();
+
     // Let's wipe all existing statistics.
     // Let's wipe all existing statistics.
     isc::stats::StatsMgr::instance().removeAll();
     isc::stats::StatsMgr::instance().removeAll();
 }
 }

+ 0 - 1
src/bin/dhcp4/tests/dhcp4_test_utils.h

@@ -194,7 +194,6 @@ public:
     using Dhcpv4Srv::acceptServerId;
     using Dhcpv4Srv::acceptServerId;
     using Dhcpv4Srv::sanityCheck;
     using Dhcpv4Srv::sanityCheck;
     using Dhcpv4Srv::srvidToString;
     using Dhcpv4Srv::srvidToString;
-    using Dhcpv4Srv::unpackOptions;
     using Dhcpv4Srv::classifyPacket;
     using Dhcpv4Srv::classifyPacket;
     using Dhcpv4Srv::accept;
     using Dhcpv4Srv::accept;
     using Dhcpv4Srv::acceptMessageType;
     using Dhcpv4Srv::acceptMessageType;

+ 6 - 121
src/bin/dhcp6/dhcp6_srv.cc

@@ -469,15 +469,6 @@ void Dhcpv6Srv::run_one() {
 
 
 void
 void
 Dhcpv6Srv::processPacket(Pkt6Ptr& query, Pkt6Ptr& rsp) {
 Dhcpv6Srv::processPacket(Pkt6Ptr& query, Pkt6Ptr& rsp) {
-    // In order to parse the DHCP options, the server needs to use some
-    // configuration information such as: existing option spaces, option
-    // definitions etc. This is the kind of information which is not
-    // available in the libdhcp, so we need to supply our own implementation
-    // of the option parsing function here, which would rely on the
-    // configuration data.
-    query->setCallback(boost::bind(&Dhcpv6Srv::unpackOptions, this, _1, _2,
-                                   _3, _4, _5));
-
     bool skip_unpack = false;
     bool skip_unpack = false;
 
 
     // The packet has just been received so contains the uninterpreted wire
     // The packet has just been received so contains the uninterpreted wire
@@ -1604,11 +1595,9 @@ Dhcpv6Srv::extendIA_NA(const Pkt6Ptr& query, const Pkt6Ptr& answer,
         Option6IAAddrPtr iaaddr = boost::dynamic_pointer_cast<Option6IAAddr>(it->second);
         Option6IAAddrPtr iaaddr = boost::dynamic_pointer_cast<Option6IAAddr>(it->second);
         if (!iaaddr) {
         if (!iaaddr) {
             // That's weird. Option code was ok, but the object type was not.
             // That's weird. Option code was ok, but the object type was not.
-            // As we use Dhcpv6Srv::unpackOptions() that is guaranteed to use
-            // Option6IAAddr for D6O_IAADDR, this should never happen. The only
-            // case would be with badly mis-implemented hook libraries that
-            // insert invalid option objects. There's no way to protect against
-            // this.
+            // This should never happen. The only case would be with badly
+            // mis-implemented hook libraries that insert invalid option objects.
+            // There's no way to protect against this.
             continue;
             continue;
         }
         }
         ctx.hints_.push_back(make_pair(iaaddr->getAddress(), 128));
         ctx.hints_.push_back(make_pair(iaaddr->getAddress(), 128));
@@ -1758,11 +1747,9 @@ Dhcpv6Srv::extendIA_PD(const Pkt6Ptr& query,
         Option6IAPrefixPtr prf = boost::dynamic_pointer_cast<Option6IAPrefix>(it->second);
         Option6IAPrefixPtr prf = boost::dynamic_pointer_cast<Option6IAPrefix>(it->second);
         if (!prf) {
         if (!prf) {
             // That's weird. Option code was ok, but the object type was not.
             // That's weird. Option code was ok, but the object type was not.
-            // As we use Dhcpv6Srv::unpackOptions() that is guaranteed to use
-            // Option6IAPrefix for D6O_IAPREFIX, this should never happen. The only
-            // case would be with badly mis-implemented hook libraries that
-            // insert invalid option objects. There's no way to protect against
-            // this.
+            // This should never happen. The only case would be with badly
+            // mis-implemented hook libraries that insert invalid option objects.
+            // There's no way to protect against this.
             continue;
             continue;
         }
         }
 
 
@@ -2779,108 +2766,6 @@ Dhcpv6Srv::processInfRequest(const Pkt6Ptr& inf_request) {
     return (reply);
     return (reply);
 }
 }
 
 
-size_t
-Dhcpv6Srv::unpackOptions(const OptionBuffer& buf,
-                         const std::string& option_space,
-                         isc::dhcp::OptionCollection& options,
-                         size_t* relay_msg_offset,
-                         size_t* relay_msg_len) {
-    size_t offset = 0;
-    size_t length = buf.size();
-
-    OptionDefContainer option_defs;
-    if (option_space == "dhcp6") {
-        // Get the list of standard option definitions.
-        option_defs = LibDHCP::getOptionDefs(Option::V6);
-    } else if (!option_space.empty()) {
-        OptionDefContainerPtr option_defs_ptr =
-            CfgMgr::instance().getCurrentCfg()->getCfgOptionDef()->
-            getAll(option_space);
-        if (option_defs_ptr != NULL) {
-            option_defs = *option_defs_ptr;
-        }
-    }
-
-    // Get the search index #1. It allows to search for option definitions
-    // using option code.
-    const OptionDefContainerTypeIndex& idx = option_defs.get<1>();
-
-    // The buffer being read comprises a set of options, each starting with
-    // a two-byte type code and a two-byte length field.
-    while (offset + 4 <= length) {
-        // At this point, from the while condition, we know that there
-        // are at least 4 bytes available following offset in the
-        // buffer.
-        uint16_t opt_type = isc::util::readUint16(&buf[offset], 2);
-        offset += 2;
-
-        uint16_t opt_len = isc::util::readUint16(&buf[offset], 2);
-        offset += 2;
-
-        if (offset + opt_len > length) {
-            // @todo: consider throwing exception here.
-
-            // We peeked at the option header of the next option, but discovered
-            // that it would end up beyond buffer end, so the option is
-            // truncated. Hence we can't parse it. Therefore we revert
-            // by by those four bytes (as if we never parsed them).
-            return (offset - 4);
-        }
-
-        if (opt_type == D6O_RELAY_MSG && relay_msg_offset && relay_msg_len) {
-            // remember offset of the beginning of the relay-msg option
-            *relay_msg_offset = offset;
-            *relay_msg_len = opt_len;
-
-            // do not create that relay-msg option
-            offset += opt_len;
-            continue;
-        }
-
-        // Get all definitions with the particular option code. Note that option
-        // code is non-unique within this container however at this point we
-        // expect to get one option definition with the particular code. If more
-        // are returned we report an error.
-        const OptionDefContainerTypeRange& range = idx.equal_range(opt_type);
-        // Get the number of returned option definitions for the option code.
-        size_t num_defs = distance(range.first, range.second);
-
-        OptionPtr opt;
-        if (num_defs > 1) {
-            // Multiple options of the same code are not supported right now!
-            isc_throw(isc::Unexpected, "Internal error: multiple option definitions"
-                      " for option type " << opt_type << " returned. Currently it is not"
-                      " supported to initialize multiple option definitions"
-                      " for the same option code. This will be supported once"
-                      " support for option spaces is implemented");
-        } else if (num_defs == 0) {
-            // @todo Don't crash if definition does not exist because only a few
-            // option definitions are initialized right now. In the future
-            // we will initialize definitions for all options and we will
-            // remove this elseif. For now, return generic option.
-            opt = OptionPtr(new Option(Option::V6, opt_type,
-                                       buf.begin() + offset,
-                                       buf.begin() + offset + opt_len));
-            opt->setEncapsulatedSpace("dhcp6");
-        } else {
-            // The option definition has been found. Use it to create
-            // the option instance from the provided buffer chunk.
-            const OptionDefinitionPtr& def = *(range.first);
-            assert(def);
-            opt = def->optionFactory(Option::V6, opt_type,
-                                     buf.begin() + offset,
-                                     buf.begin() + offset + opt_len,
-                                     boost::bind(&Dhcpv6Srv::unpackOptions, this, _1, _2,
-                                                 _3, _4, _5));
-        }
-        // add option to options
-        options.insert(std::make_pair(opt_type, opt));
-        offset += opt_len;
-    }
-
-    return (offset);
-}
-
 void Dhcpv6Srv::classifyByVendor(const Pkt6Ptr& pkt, std::string& classes) {
 void Dhcpv6Srv::classifyByVendor(const Pkt6Ptr& pkt, std::string& classes) {
     OptionVendorClassPtr vclass = boost::dynamic_pointer_cast<
     OptionVendorClassPtr vclass = boost::dynamic_pointer_cast<
         OptionVendorClass>(pkt->getOption(D6O_VENDOR_CLASS));
         OptionVendorClass>(pkt->getOption(D6O_VENDOR_CLASS));

+ 0 - 18
src/bin/dhcp6/dhcp6_srv.h

@@ -607,24 +607,6 @@ protected:
     /// simulates transmission of a packet. For that purpose it is protected.
     /// simulates transmission of a packet. For that purpose it is protected.
     virtual void sendPacket(const Pkt6Ptr& pkt);
     virtual void sendPacket(const Pkt6Ptr& pkt);
 
 
-    /// @brief Implements a callback function to parse options in the message.
-    ///
-    /// @param buf a A buffer holding options in on-wire format.
-    /// @param option_space A name of the option space which holds definitions
-    /// of to be used to parse options in the packets.
-    /// @param [out] options A reference to the collection where parsed options
-    /// will be stored.
-    /// @param relay_msg_offset Reference to a size_t structure. If specified,
-    /// offset to beginning of relay_msg option will be stored in it.
-    /// @param relay_msg_len reference to a size_t structure. If specified,
-    /// length of the relay_msg option will be stored in it.
-    /// @return An offset to the first byte after last parsed option.
-    size_t unpackOptions(const OptionBuffer& buf,
-                         const std::string& option_space,
-                         isc::dhcp::OptionCollection& options,
-                         size_t* relay_msg_offset,
-                         size_t* relay_msg_len);
-
     /// @brief Assigns incoming packet to zero or more classes.
     /// @brief Assigns incoming packet to zero or more classes.
     ///
     ///
     /// @note This is done in two phases: first the content of the
     /// @note This is done in two phases: first the content of the

+ 1 - 74
src/bin/dhcp6/tests/dhcp6_srv_unittest.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2011-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -1721,79 +1721,6 @@ TEST_F(Dhcpv6SrvTest, vendorOptionsDocsisDefinitions) {
     ASSERT_EQ(0, rcode_);
     ASSERT_EQ(0, rcode_);
 }
 }
 
 
-// This test verifies that the following option structure can be parsed:
-// - option (option space 'foobar')
-//   - sub option (option space 'foo')
-//      - sub option (option space 'bar')
-TEST_F(Dhcpv6SrvTest, unpackOptions) {
-    // Create option definition for each level of encapsulation. Each option
-    // definition is for the option code 1. Options may have the same
-    // option code because they belong to different option spaces.
-
-    // Top level option encapsulates options which belong to 'space-foo'.
-    OptionDefinitionPtr opt_def(new OptionDefinition("option-foobar", 1, "uint32",
-                                                      "space-foo"));\
-    // Middle option encapsulates options which belong to 'space-bar'
-    OptionDefinitionPtr opt_def2(new OptionDefinition("option-foo", 1, "uint16",
-                                                      "space-bar"));
-    // Low level option doesn't encapsulate any option space.
-    OptionDefinitionPtr opt_def3(new OptionDefinition("option-bar", 1,
-                                                      "uint8"));
-
-    // Add option definitions to the Configuration Manager. Each goes under
-    // different option space.
-    CfgOptionDefPtr cfg_option_def =
-        CfgMgr::instance().getStagingCfg()->getCfgOptionDef();
-    ASSERT_NO_THROW(cfg_option_def->add(opt_def, "space-foobar"));
-    ASSERT_NO_THROW(cfg_option_def->add(opt_def2, "space-foo"));
-    ASSERT_NO_THROW(cfg_option_def->add(opt_def3, "space-bar"));
-    CfgMgr::instance().commit();
-
-    // Create the buffer holding the structure of options.
-    const char raw_data[] = {
-        // First option starts here.
-        0x00, 0x01,   // option code = 1
-        0x00, 0x0F,   // option length = 15
-        0x00, 0x01, 0x02, 0x03, // This option carries uint32 value
-        // Sub option starts here.
-        0x00, 0x01,  // option code = 1
-        0x00, 0x07,  // option length = 7
-        0x01, 0x02,  // this option carries uint16 value
-        // Last option starts here.
-        0x00, 0x01,  // option code = 1
-        0x00, 0x01,  // option length = 1
-        0x00 // This option carries a single uint8 value and has no sub options.
-    };
-    OptionBuffer buf(raw_data, raw_data + sizeof(raw_data));
-
-    // Parse options.
-    NakedDhcpv6Srv srv(0);
-    OptionCollection options;
-    ASSERT_NO_THROW(srv.unpackOptions(buf, "space-foobar", options, 0, 0));
-
-    // There should be one top level option.
-    ASSERT_EQ(1, options.size());
-    boost::shared_ptr<OptionInt<uint32_t> > option_foobar =
-        boost::dynamic_pointer_cast<OptionInt<uint32_t> >(options.begin()->
-                                                          second);
-    ASSERT_TRUE(option_foobar);
-    EXPECT_EQ(1, option_foobar->getType());
-    EXPECT_EQ(0x00010203, option_foobar->getValue());
-    // There should be a middle level option held in option_foobar.
-    boost::shared_ptr<OptionInt<uint16_t> > option_foo =
-        boost::dynamic_pointer_cast<OptionInt<uint16_t> >(option_foobar->
-                                                          getOption(1));
-    ASSERT_TRUE(option_foo);
-    EXPECT_EQ(1, option_foo->getType());
-    EXPECT_EQ(0x0102, option_foo->getValue());
-    // Finally, there should be a low level option under option_foo.
-    boost::shared_ptr<OptionInt<uint8_t> > option_bar =
-        boost::dynamic_pointer_cast<OptionInt<uint8_t> >(option_foo->getOption(1));
-    ASSERT_TRUE(option_bar);
-    EXPECT_EQ(1, option_bar->getType());
-    EXPECT_EQ(0x0, option_bar->getValue());
-}
-
 // Checks if DOCSIS client packets are classified properly
 // Checks if DOCSIS client packets are classified properly
 TEST_F(Dhcpv6SrvTest, docsisClientClassification) {
 TEST_F(Dhcpv6SrvTest, docsisClientClassification) {
 
 

+ 1 - 2
src/bin/dhcp6/tests/dhcp6_test_utils.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -127,7 +127,6 @@ public:
     using Dhcpv6Srv::testUnicast;
     using Dhcpv6Srv::testUnicast;
     using Dhcpv6Srv::sanityCheck;
     using Dhcpv6Srv::sanityCheck;
     using Dhcpv6Srv::classifyPacket;
     using Dhcpv6Srv::classifyPacket;
-    using Dhcpv6Srv::unpackOptions;
     using Dhcpv6Srv::shutdown_;
     using Dhcpv6Srv::shutdown_;
     using Dhcpv6Srv::name_change_reqs_;
     using Dhcpv6Srv::name_change_reqs_;
     using Dhcpv6Srv::VENDOR_CLASS_PREFIX;
     using Dhcpv6Srv::VENDOR_CLASS_PREFIX;

+ 82 - 65
src/lib/dhcp/libdhcp++.cc

@@ -15,6 +15,7 @@
 #include <dhcp/option6_iaaddr.h>
 #include <dhcp/option6_iaaddr.h>
 #include <dhcp/option_definition.h>
 #include <dhcp/option_definition.h>
 #include <dhcp/option_int_array.h>
 #include <dhcp/option_int_array.h>
+#include <dhcp/option_space.h>
 #include <dhcp/std_option_defs.h>
 #include <dhcp/std_option_defs.h>
 #include <dhcp/docsis3_option_defs.h>
 #include <dhcp/docsis3_option_defs.h>
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
@@ -39,10 +40,10 @@ std::map<unsigned short, Option::Factory*> LibDHCP::v4factories_;
 std::map<unsigned short, Option::Factory*> LibDHCP::v6factories_;
 std::map<unsigned short, Option::Factory*> LibDHCP::v6factories_;
 
 
 // Static container with DHCPv4 option definitions.
 // Static container with DHCPv4 option definitions.
-OptionDefContainer LibDHCP::v4option_defs_;
+OptionDefContainerPtr LibDHCP::v4option_defs_(new OptionDefContainer());
 
 
 // Static container with DHCPv6 option definitions.
 // Static container with DHCPv6 option definitions.
-OptionDefContainer LibDHCP::v6option_defs_;
+OptionDefContainerPtr LibDHCP::v6option_defs_(new OptionDefContainer());
 
 
 VendorOptionDefContainers LibDHCP::vendor4_defs_;
 VendorOptionDefContainers LibDHCP::vendor4_defs_;
 
 
@@ -51,6 +52,8 @@ VendorOptionDefContainers LibDHCP::vendor6_defs_;
 // Static container with option definitions created in runtime.
 // Static container with option definitions created in runtime.
 StagedValue<OptionDefSpaceContainer> LibDHCP::runtime_option_defs_;
 StagedValue<OptionDefSpaceContainer> LibDHCP::runtime_option_defs_;
 
 
+// Null container.
+const OptionDefContainerPtr null_option_def_container_(new OptionDefContainer());
 
 
 // Those two vendor classes are used for cable modems:
 // Those two vendor classes are used for cable modems:
 
 
@@ -62,21 +65,21 @@ const char* isc::dhcp::DOCSIS3_CLASS_EROUTER = "eRouter1.0";
 
 
 // Let's keep it in .cc file. Moving it to .h would require including optionDefParams
 // Let's keep it in .cc file. Moving it to .h would require including optionDefParams
 // definitions there
 // definitions there
-void initOptionSpace(OptionDefContainer& defs,
+void initOptionSpace(OptionDefContainerPtr& defs,
                      const OptionDefParams* params,
                      const OptionDefParams* params,
                      size_t params_size);
                      size_t params_size);
 
 
-const OptionDefContainer&
+const OptionDefContainerPtr&
 LibDHCP::getOptionDefs(const Option::Universe u) {
 LibDHCP::getOptionDefs(const Option::Universe u) {
     switch (u) {
     switch (u) {
     case Option::V4:
     case Option::V4:
-        if (v4option_defs_.empty()) {
+        if (v4option_defs_->empty()) {
             initStdOptionDefs4();
             initStdOptionDefs4();
             initVendorOptsDocsis4();
             initVendorOptsDocsis4();
         }
         }
         return (v4option_defs_);
         return (v4option_defs_);
     case Option::V6:
     case Option::V6:
-        if (v6option_defs_.empty()) {
+        if (v6option_defs_->empty()) {
             initStdOptionDefs6();
             initStdOptionDefs6();
             initVendorOptsDocsis6();
             initVendorOptsDocsis6();
         }
         }
@@ -86,7 +89,7 @@ LibDHCP::getOptionDefs(const Option::Universe u) {
     }
     }
 }
 }
 
 
-const OptionDefContainer*
+const OptionDefContainerPtr&
 LibDHCP::getVendorOption4Defs(const uint32_t vendor_id) {
 LibDHCP::getVendorOption4Defs(const uint32_t vendor_id) {
 
 
     if (vendor_id == VENDOR_ID_CABLE_LABS &&
     if (vendor_id == VENDOR_ID_CABLE_LABS &&
@@ -97,12 +100,12 @@ LibDHCP::getVendorOption4Defs(const uint32_t vendor_id) {
     VendorOptionDefContainers::const_iterator def = vendor4_defs_.find(vendor_id);
     VendorOptionDefContainers::const_iterator def = vendor4_defs_.find(vendor_id);
     if (def == vendor4_defs_.end()) {
     if (def == vendor4_defs_.end()) {
         // No such vendor-id space
         // No such vendor-id space
-        return (NULL);
+        return (null_option_def_container_);
     }
     }
-    return (&(def->second));
+    return (def->second);
 }
 }
 
 
-const OptionDefContainer*
+const OptionDefContainerPtr&
 LibDHCP::getVendorOption6Defs(const uint32_t vendor_id) {
 LibDHCP::getVendorOption6Defs(const uint32_t vendor_id) {
 
 
     if (vendor_id == VENDOR_ID_CABLE_LABS &&
     if (vendor_id == VENDOR_ID_CABLE_LABS &&
@@ -118,15 +121,15 @@ LibDHCP::getVendorOption6Defs(const uint32_t vendor_id) {
     VendorOptionDefContainers::const_iterator def = vendor6_defs_.find(vendor_id);
     VendorOptionDefContainers::const_iterator def = vendor6_defs_.find(vendor_id);
     if (def == vendor6_defs_.end()) {
     if (def == vendor6_defs_.end()) {
         // No such vendor-id space
         // No such vendor-id space
-        return (NULL);
+        return (null_option_def_container_);
     }
     }
-    return (&(def->second));
+    return (def->second);
 }
 }
 
 
 OptionDefinitionPtr
 OptionDefinitionPtr
 LibDHCP::getOptionDef(const Option::Universe u, const uint16_t code) {
 LibDHCP::getOptionDef(const Option::Universe u, const uint16_t code) {
-    const OptionDefContainer& defs = getOptionDefs(u);
-    const OptionDefContainerTypeIndex& idx = defs.get<1>();
+    const OptionDefContainerPtr& defs = getOptionDefs(u);
+    const OptionDefContainerTypeIndex& idx = defs->get<1>();
     const OptionDefContainerTypeRange& range = idx.equal_range(code);
     const OptionDefContainerTypeRange& range = idx.equal_range(code);
     if (range.first != range.second) {
     if (range.first != range.second) {
         return (*range.first);
         return (*range.first);
@@ -136,8 +139,8 @@ LibDHCP::getOptionDef(const Option::Universe u, const uint16_t code) {
 
 
 OptionDefinitionPtr
 OptionDefinitionPtr
 LibDHCP::getOptionDef(const Option::Universe u, const std::string& name) {
 LibDHCP::getOptionDef(const Option::Universe u, const std::string& name) {
-    const OptionDefContainer& defs = getOptionDefs(u);
-    const OptionDefContainerNameIndex& idx = defs.get<2>();
+    const OptionDefContainerPtr defs = getOptionDefs(u);
+    const OptionDefContainerNameIndex& idx = defs->get<2>();
     const OptionDefContainerNameRange& range = idx.equal_range(name);
     const OptionDefContainerNameRange& range = idx.equal_range(name);
     if (range.first != range.second) {
     if (range.first != range.second) {
         return (*range.first);
         return (*range.first);
@@ -150,12 +153,8 @@ LibDHCP::getOptionDef(const Option::Universe u, const std::string& name) {
 OptionDefinitionPtr
 OptionDefinitionPtr
 LibDHCP::getVendorOptionDef(const Option::Universe u, const uint32_t vendor_id,
 LibDHCP::getVendorOptionDef(const Option::Universe u, const uint32_t vendor_id,
                             const std::string& name) {
                             const std::string& name) {
-    const OptionDefContainer* defs = NULL;
-    if (u == Option::V4) {
-        defs = getVendorOption4Defs(vendor_id);
-    } else if (u == Option::V6) {
-        defs = getVendorOption6Defs(vendor_id);
-    }
+    OptionDefContainerPtr defs = (u == Option::V4 ? getVendorOption4Defs(vendor_id) :
+                                  getVendorOption6Defs(vendor_id));
 
 
     if (!defs) {
     if (!defs) {
         return (OptionDefinitionPtr());
         return (OptionDefinitionPtr());
@@ -172,12 +171,8 @@ LibDHCP::getVendorOptionDef(const Option::Universe u, const uint32_t vendor_id,
 OptionDefinitionPtr
 OptionDefinitionPtr
 LibDHCP::getVendorOptionDef(const Option::Universe u, const uint32_t vendor_id,
 LibDHCP::getVendorOptionDef(const Option::Universe u, const uint32_t vendor_id,
                             const uint16_t code) {
                             const uint16_t code) {
-    const OptionDefContainer* defs = NULL;
-    if (u == Option::V4) {
-        defs = getVendorOption4Defs(vendor_id);
-    } else if (u == Option::V6) {
-        defs = getVendorOption6Defs(vendor_id);
-    }
+    OptionDefContainerPtr defs = (u == Option::V4 ? getVendorOption4Defs(vendor_id) :
+                                  getVendorOption6Defs(vendor_id));
 
 
     if (!defs) {
     if (!defs) {
         // Weird universe or unknown vendor_id. We don't care. No definitions
         // Weird universe or unknown vendor_id. We don't care. No definitions
@@ -317,24 +312,19 @@ size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
     size_t last_offset = 0;
     size_t last_offset = 0;
 
 
     // Get the list of standard option definitions.
     // Get the list of standard option definitions.
-    OptionDefContainer option_defs;
-    if (option_space == "dhcp6") {
-        option_defs = LibDHCP::getOptionDefs(Option::V6);
-    } else {
-        OptionDefContainerPtr option_defs_ptr =
-            LibDHCP::getRuntimeOptionDefs(option_space);
-        if (option_defs_ptr) {
-            option_defs = *option_defs_ptr;
-        }
-    }
+    const OptionDefContainerPtr& option_defs = LibDHCP::getOptionDefs(Option::V6);
+    // Runtime option definitions for non standard option space and if
+    // the definition doesn't exist within the standard option definitions.
+    const OptionDefContainerPtr& runtime_option_defs = LibDHCP::getRuntimeOptionDefs(option_space);
 
 
     // @todo Once we implement other option spaces we should add else clause
     // @todo Once we implement other option spaces we should add else clause
     // here and gather option definitions for them. For now leaving option_defs
     // here and gather option definitions for them. For now leaving option_defs
     // empty will imply creation of generic Option.
     // empty will imply creation of generic Option.
 
 
-    // Get the search index #1. It allows to search for option definitions
+    // Get the search indexes #1. It allows to search for option definitions
     // using option code.
     // using option code.
-    const OptionDefContainerTypeIndex& idx = option_defs.get<1>();
+    const OptionDefContainerTypeIndex& idx = option_defs->get<1>();
+    const OptionDefContainerTypeIndex& runtime_idx = runtime_option_defs->get<1>();
 
 
     // The buffer being read comprises a set of options, each starting with
     // The buffer being read comprises a set of options, each starting with
     // a two-byte type code and a two-byte length field.
     // a two-byte type code and a two-byte length field.
@@ -399,9 +389,21 @@ size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
         // however at this point we expect to get one option
         // however at this point we expect to get one option
         // definition with the particular code. If more are returned
         // definition with the particular code. If more are returned
         // we report an error.
         // we report an error.
-        const OptionDefContainerTypeRange& range = idx.equal_range(opt_type);
-        // Get the number of returned option definitions for the option code.
-        size_t num_defs = distance(range.first, range.second);
+        OptionDefContainerTypeRange range;
+        // Number of option definitions returned.
+        size_t num_defs = 0;
+        if (option_space == DHCP6_OPTION_SPACE) {
+            range = idx.equal_range(opt_type);
+            num_defs = distance(range.first, range.second);
+        }
+
+        // Standard option definitions do not include the definition for
+        // our option or we're searching for non-standard option. Try to
+        // find the definition among runtime option definitions.
+        if (num_defs == 0) {
+            range = runtime_idx.equal_range(opt_type);
+            num_defs = distance(range.first, range.second);
+        }
 
 
         OptionPtr opt;
         OptionPtr opt;
         if (num_defs > 1) {
         if (num_defs > 1) {
@@ -446,17 +448,15 @@ size_t LibDHCP::unpackOptions4(const OptionBuffer& buf,
     size_t last_offset = 0;
     size_t last_offset = 0;
 
 
     // Get the list of standard option definitions.
     // Get the list of standard option definitions.
-    OptionDefContainer option_defs;
-    if (option_space == "dhcp4") {
-        option_defs = LibDHCP::getOptionDefs(Option::V4);
-    }
-    // @todo Once we implement other option spaces we should add else clause
-    // here and gather option definitions for them. For now leaving option_defs
-    // empty will imply creation of generic Option.
+    const OptionDefContainerPtr& option_defs = LibDHCP::getOptionDefs(Option::V4);
+    // Runtime option definitions for non standard option space and if
+    // the definition doesn't exist within the standard option definitions.
+    const OptionDefContainerPtr& runtime_option_defs = LibDHCP::getRuntimeOptionDefs(option_space);
 
 
-    // Get the search index #1. It allows to search for option definitions
+    // Get the search indexes #1. It allows to search for option definitions
     // using option code.
     // using option code.
-    const OptionDefContainerTypeIndex& idx = option_defs.get<1>();
+    const OptionDefContainerTypeIndex& idx = option_defs->get<1>();
+    const OptionDefContainerTypeIndex& runtime_idx = runtime_option_defs->get<1>();
 
 
     // The buffer being read comprises a set of options, each starting with
     // The buffer being read comprises a set of options, each starting with
     // a one-byte type code and a one-byte length field.
     // a one-byte type code and a one-byte length field.
@@ -505,9 +505,21 @@ size_t LibDHCP::unpackOptions4(const OptionBuffer& buf,
         // however at this point we expect to get one option
         // however at this point we expect to get one option
         // definition with the particular code. If more are returned
         // definition with the particular code. If more are returned
         // we report an error.
         // we report an error.
-        const OptionDefContainerTypeRange& range = idx.equal_range(opt_type);
-        // Get the number of returned option definitions for the option code.
-        size_t num_defs = distance(range.first, range.second);
+        OptionDefContainerTypeRange range;
+        // Number of option definitions returned.
+        size_t num_defs = 0;
+        if (option_space == DHCP4_OPTION_SPACE) {
+            range = idx.equal_range(opt_type);
+            num_defs = distance(range.first, range.second);
+        }
+
+        // Standard option definitions do not include the definition for
+        // our option or we're searching for non-standard option. Try to
+        // find the definition among runtime option definitions.
+        if (num_defs == 0) {
+            range = runtime_idx.equal_range(opt_type);
+            num_defs = distance(range.first, range.second);
+        }
 
 
         OptionPtr opt;
         OptionPtr opt;
         if (num_defs > 1) {
         if (num_defs > 1) {
@@ -523,6 +535,7 @@ size_t LibDHCP::unpackOptions4(const OptionBuffer& buf,
             opt = OptionPtr(new Option(Option::V4, opt_type,
             opt = OptionPtr(new Option(Option::V4, opt_type,
                                        buf.begin() + offset,
                                        buf.begin() + offset,
                                        buf.begin() + offset + opt_len));
                                        buf.begin() + offset + opt_len));
+            opt->setEncapsulatedSpace(DHCP4_OPTION_SPACE);
         } else {
         } else {
             // The option definition has been found. Use it to create
             // The option definition has been found. Use it to create
             // the option instance from the provided buffer chunk.
             // the option instance from the provided buffer chunk.
@@ -547,8 +560,7 @@ size_t LibDHCP::unpackVendorOptions6(const uint32_t vendor_id,
     size_t length = buf.size();
     size_t length = buf.size();
 
 
     // Get the list of option definitions for this particular vendor-id
     // Get the list of option definitions for this particular vendor-id
-    const OptionDefContainer* option_defs =
-        LibDHCP::getVendorOption6Defs(vendor_id);
+    const OptionDefContainerPtr& option_defs = LibDHCP::getVendorOption6Defs(vendor_id);
 
 
     // Get the search index #1. It allows to search for option definitions
     // Get the search index #1. It allows to search for option definitions
     // using option code. If there's no such vendor-id space, we're out of luck
     // using option code. If there's no such vendor-id space, we're out of luck
@@ -640,8 +652,7 @@ size_t LibDHCP::unpackVendorOptions4(const uint32_t vendor_id, const OptionBuffe
     size_t offset = 0;
     size_t offset = 0;
 
 
     // Get the list of stdandard option definitions.
     // Get the list of stdandard option definitions.
-    const OptionDefContainer* option_defs =
-        LibDHCP::getVendorOption4Defs(vendor_id);
+    const OptionDefContainerPtr& option_defs = LibDHCP::getVendorOption4Defs(vendor_id);
     // Get the search index #1. It allows to search for option definitions
     // Get the search index #1. It allows to search for option definitions
     // using option code.
     // using option code.
     const OptionDefContainerTypeIndex* idx = NULL;
     const OptionDefContainerTypeIndex* idx = NULL;
@@ -836,13 +847,11 @@ LibDHCP::initVendorOptsDocsis4() {
 
 
 void
 void
 LibDHCP::initVendorOptsDocsis6() {
 LibDHCP::initVendorOptsDocsis6() {
-    vendor6_defs_[VENDOR_ID_CABLE_LABS] = OptionDefContainer();
     initOptionSpace(vendor6_defs_[VENDOR_ID_CABLE_LABS], DOCSIS3_V6_DEFS, DOCSIS3_V6_DEFS_SIZE);
     initOptionSpace(vendor6_defs_[VENDOR_ID_CABLE_LABS], DOCSIS3_V6_DEFS, DOCSIS3_V6_DEFS_SIZE);
 }
 }
 
 
 void
 void
 LibDHCP::initVendorOptsIsc6() {
 LibDHCP::initVendorOptsIsc6() {
-    vendor6_defs_[ENTERPRISE_ID_ISC] = OptionDefContainer();
     initOptionSpace(vendor6_defs_[ENTERPRISE_ID_ISC], ISC_V6_DEFS, ISC_V6_DEFS_SIZE);
     initOptionSpace(vendor6_defs_[ENTERPRISE_ID_ISC], ISC_V6_DEFS, ISC_V6_DEFS_SIZE);
 }
 }
 
 
@@ -872,10 +881,18 @@ LibDHCP::optionSpaceToVendorId(const std::string& option_space) {
     return (static_cast<uint32_t>(check));
     return (static_cast<uint32_t>(check));
 }
 }
 
 
-void initOptionSpace(OptionDefContainer& defs,
+void initOptionSpace(OptionDefContainerPtr& defs,
                      const OptionDefParams* params,
                      const OptionDefParams* params,
                      size_t params_size) {
                      size_t params_size) {
-    defs.clear();
+    // Container holding vendor options is typically not initialized, as it
+    // is held in map of null pointers. We need to initialize here in this
+    // case.
+    if (!defs) {
+        defs.reset(new OptionDefContainer());
+
+    } else {
+        defs->clear();
+    }
 
 
     for (size_t i = 0; i < params_size; ++i) {
     for (size_t i = 0; i < params_size; ++i) {
         std::string encapsulates(params[i].encapsulates);
         std::string encapsulates(params[i].encapsulates);
@@ -918,9 +935,9 @@ void initOptionSpace(OptionDefContainer& defs,
             // be only caused by programming error. To guarantee the
             // be only caused by programming error. To guarantee the
             // data consistency we clear all option definitions that
             // data consistency we clear all option definitions that
             // have been added so far and pass the exception forward.
             // have been added so far and pass the exception forward.
-            defs.clear();
+            defs->clear();
             throw;
             throw;
         }
         }
-        defs.push_back(definition);
+        defs->push_back(definition);
     }
     }
 }
 }

+ 8 - 8
src/lib/dhcp/libdhcp++.h

@@ -37,8 +37,8 @@ public:
     ///
     ///
     /// @param u universe of the options (V4 or V6).
     /// @param u universe of the options (V4 or V6).
     ///
     ///
-    /// @return collection of option definitions.
-    static const OptionDefContainer& getOptionDefs(const Option::Universe u);
+    /// @return Pointer to a collection of option definitions.
+    static const OptionDefContainerPtr& getOptionDefs(const Option::Universe u);
 
 
     /// @brief Return the first option definition matching a
     /// @brief Return the first option definition matching a
     /// particular option code.
     /// particular option code.
@@ -251,17 +251,17 @@ public:
     /// @brief Returns v4 option definitions for a given vendor
     /// @brief Returns v4 option definitions for a given vendor
     ///
     ///
     /// @param vendor_id enterprise-id of a given vendor
     /// @param vendor_id enterprise-id of a given vendor
-    /// @return a container for a given vendor (or NULL if not option
+    /// @return a container for a given vendor (or NULL if no option
     ///         definitions are defined)
     ///         definitions are defined)
-    static const OptionDefContainer*
+    static const OptionDefContainerPtr&
     getVendorOption4Defs(const uint32_t vendor_id);
     getVendorOption4Defs(const uint32_t vendor_id);
 
 
     /// @brief Returns v6 option definitions for a given vendor
     /// @brief Returns v6 option definitions for a given vendor
     ///
     ///
     /// @param vendor_id enterprise-id of a given vendor
     /// @param vendor_id enterprise-id of a given vendor
-    /// @return a container for a given vendor (or NULL if not option
+    /// @return a container for a given vendor (or NULL if no option
     ///         definitions are defined)
     ///         definitions are defined)
-    static const OptionDefContainer*
+    static const OptionDefContainerPtr&
     getVendorOption6Defs(const uint32_t vendor_id);
     getVendorOption6Defs(const uint32_t vendor_id);
 
 
     /// @brief Parses provided buffer as DHCPv6 vendor options and creates
     /// @brief Parses provided buffer as DHCPv6 vendor options and creates
@@ -372,10 +372,10 @@ private:
     static FactoryMap v6factories_;
     static FactoryMap v6factories_;
 
 
     /// Container with DHCPv4 option definitions.
     /// Container with DHCPv4 option definitions.
-    static OptionDefContainer v4option_defs_;
+    static OptionDefContainerPtr v4option_defs_;
 
 
     /// Container with DHCPv6 option definitions.
     /// Container with DHCPv6 option definitions.
-    static OptionDefContainer v6option_defs_;
+    static OptionDefContainerPtr v6option_defs_;
 
 
     /// Container for v4 vendor option definitions
     /// Container for v4 vendor option definitions
     static VendorOptionDefContainers vendor4_defs_;
     static VendorOptionDefContainers vendor4_defs_;

+ 1 - 8
src/lib/dhcp/option.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2011-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -129,13 +129,6 @@ void Option::unpack(OptionBufferConstIter begin,
 
 
 void
 void
 Option::unpackOptions(const OptionBuffer& buf) {
 Option::unpackOptions(const OptionBuffer& buf) {
-    // If custom option parsing function has been set, use this function
-    // to parse options. Otherwise, use standard function from libdhcp++.
-    if (!callback_.empty()) {
-        callback_(buf, getEncapsulatedSpace(), options_, 0, 0);
-        return;
-    }
-
     switch (universe_) {
     switch (universe_) {
     case V4:
     case V4:
         LibDHCP::unpackOptions4(buf, getEncapsulatedSpace(), options_);
         LibDHCP::unpackOptions4(buf, getEncapsulatedSpace(), options_);

+ 1 - 38
src/lib/dhcp/option.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2011-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -42,32 +42,6 @@ typedef std::multimap<unsigned int, OptionPtr> OptionCollection;
 /// A pointer to an OptionCollection
 /// A pointer to an OptionCollection
 typedef boost::shared_ptr<OptionCollection> OptionCollectionPtr;
 typedef boost::shared_ptr<OptionCollection> OptionCollectionPtr;
 
 
-/// @brief This type describes a callback function to parse options from buffer.
-///
-/// @note The last two parameters should be specified in the callback function
-/// parameters list only if DHCPv6 options are parsed. Exclude these parameters
-/// from the callback function defined to parse DHCPv4 options.
-///
-/// @param buffer A buffer holding options to be parsed.
-/// @param encapsulated_space A name of the option space to which options being
-/// parsed belong.
-/// @param [out] options A container to which parsed options should be appended.
-/// @param relay_msg_offset A pointer to a size_t value. It indicates the
-/// offset to beginning of relay_msg option. This parameter should be specified
-/// for DHCPv6 options only.
-/// @param relay_msg_len A pointer to a size_t value. It holds the length of
-/// of the relay_msg option. This parameter should be specified for DHCPv6
-/// options only.
-///
-/// @return An offset to the first byte after last parsed option.
-typedef boost::function< size_t(const OptionBuffer& buffer,
-                                const std::string encapsulated_space,
-                                OptionCollection& options,
-                                size_t* relay_msg_offset,
-                                size_t* relay_msg_len)
-                         > UnpackOptionsCallback;
-
-
 class Option {
 class Option {
 public:
 public:
     /// length of the usual DHCPv4 option header (there are exceptions)
     /// length of the usual DHCPv4 option header (there are exceptions)
@@ -361,14 +335,6 @@ public:
         return (encapsulated_space_);
         return (encapsulated_space_);
     }
     }
 
 
-    /// @brief Set callback function to be used to parse options.
-    ///
-    /// @param callback An instance of the callback function or NULL to
-    /// uninstall callback.
-    void setCallback(UnpackOptionsCallback callback) {
-        callback_ = callback;
-    }
-
     /// just to force that every option has virtual dtor
     /// just to force that every option has virtual dtor
     virtual ~Option();
     virtual ~Option();
 
 
@@ -492,9 +458,6 @@ protected:
     /// Name of the option space being encapsulated by this option.
     /// Name of the option space being encapsulated by this option.
     std::string encapsulated_space_;
     std::string encapsulated_space_;
 
 
-    /// A callback to be called to unpack options from the packet.
-    UnpackOptionsCallback callback_;
-
     /// @todo probably 2 different containers have to be used for v4 (unique
     /// @todo probably 2 different containers have to be used for v4 (unique
     /// options) and v6 (options with the same type can repeat)
     /// options) and v6 (options with the same type can repeat)
 };
 };

+ 11 - 14
src/lib/dhcp/option_definition.cc

@@ -121,8 +121,7 @@ OptionDefinition::addRecordField(const OptionDataType data_type) {
 OptionPtr
 OptionPtr
 OptionDefinition::optionFactory(Option::Universe u, uint16_t type,
 OptionDefinition::optionFactory(Option::Universe u, uint16_t type,
                                 OptionBufferConstIter begin,
                                 OptionBufferConstIter begin,
-                                OptionBufferConstIter end,
-                                UnpackOptionsCallback callback) const {
+                                OptionBufferConstIter end) const {
 
 
     try {
     try {
         // Some of the options are represented by the specialized classes derived
         // Some of the options are represented by the specialized classes derived
@@ -131,7 +130,7 @@ OptionDefinition::optionFactory(Option::Universe u, uint16_t type,
         // type to be returned. Therefore, we first check that if we are dealing
         // type to be returned. Therefore, we first check that if we are dealing
         // with such an option. If the instance is returned we just exit at this
         // with such an option. If the instance is returned we just exit at this
         // point. If not, we will search for a generic option type to return.
         // point. If not, we will search for a generic option type to return.
-        OptionPtr option = factorySpecialFormatOption(u, begin, end, callback);
+        OptionPtr option = factorySpecialFormatOption(u, begin, end);
         if (option) {
         if (option) {
             return (option);
             return (option);
         }
         }
@@ -151,37 +150,37 @@ OptionDefinition::optionFactory(Option::Universe u, uint16_t type,
             return (array_type_ ?
             return (array_type_ ?
                     factoryIntegerArray<uint8_t>(u, type, begin, end) :
                     factoryIntegerArray<uint8_t>(u, type, begin, end) :
                     factoryInteger<uint8_t>(u, type, getEncapsulatedSpace(),
                     factoryInteger<uint8_t>(u, type, getEncapsulatedSpace(),
-                                            begin, end, callback));
+                                            begin, end));
 
 
         case OPT_INT8_TYPE:
         case OPT_INT8_TYPE:
             return (array_type_ ?
             return (array_type_ ?
                     factoryIntegerArray<int8_t>(u, type, begin, end) :
                     factoryIntegerArray<int8_t>(u, type, begin, end) :
                     factoryInteger<int8_t>(u, type, getEncapsulatedSpace(),
                     factoryInteger<int8_t>(u, type, getEncapsulatedSpace(),
-                                           begin, end, callback));
+                                           begin, end));
 
 
         case OPT_UINT16_TYPE:
         case OPT_UINT16_TYPE:
             return (array_type_ ?
             return (array_type_ ?
                     factoryIntegerArray<uint16_t>(u, type, begin, end) :
                     factoryIntegerArray<uint16_t>(u, type, begin, end) :
                     factoryInteger<uint16_t>(u, type, getEncapsulatedSpace(),
                     factoryInteger<uint16_t>(u, type, getEncapsulatedSpace(),
-                                             begin, end, callback));
+                                             begin, end));
 
 
         case OPT_INT16_TYPE:
         case OPT_INT16_TYPE:
             return (array_type_ ?
             return (array_type_ ?
                     factoryIntegerArray<uint16_t>(u, type, begin, end) :
                     factoryIntegerArray<uint16_t>(u, type, begin, end) :
                     factoryInteger<int16_t>(u, type, getEncapsulatedSpace(),
                     factoryInteger<int16_t>(u, type, getEncapsulatedSpace(),
-                                            begin, end, callback));
+                                            begin, end));
 
 
         case OPT_UINT32_TYPE:
         case OPT_UINT32_TYPE:
             return (array_type_ ?
             return (array_type_ ?
                     factoryIntegerArray<uint32_t>(u, type, begin, end) :
                     factoryIntegerArray<uint32_t>(u, type, begin, end) :
                     factoryInteger<uint32_t>(u, type, getEncapsulatedSpace(),
                     factoryInteger<uint32_t>(u, type, getEncapsulatedSpace(),
-                                             begin, end, callback));
+                                             begin, end));
 
 
         case OPT_INT32_TYPE:
         case OPT_INT32_TYPE:
             return (array_type_ ?
             return (array_type_ ?
                     factoryIntegerArray<uint32_t>(u, type, begin, end) :
                     factoryIntegerArray<uint32_t>(u, type, begin, end) :
                     factoryInteger<int32_t>(u, type, getEncapsulatedSpace(),
                     factoryInteger<int32_t>(u, type, getEncapsulatedSpace(),
-                                            begin, end, callback));
+                                            begin, end));
 
 
         case OPT_IPV4_ADDRESS_TYPE:
         case OPT_IPV4_ADDRESS_TYPE:
             // If definition specifies that an option is an array
             // If definition specifies that an option is an array
@@ -217,9 +216,8 @@ OptionDefinition::optionFactory(Option::Universe u, uint16_t type,
 
 
 OptionPtr
 OptionPtr
 OptionDefinition::optionFactory(Option::Universe u, uint16_t type,
 OptionDefinition::optionFactory(Option::Universe u, uint16_t type,
-                                const OptionBuffer& buf,
-                                UnpackOptionsCallback callback) const {
-    return (optionFactory(u, type, buf.begin(), buf.end(), callback));
+                                const OptionBuffer& buf) const {
+    return (optionFactory(u, type, buf.begin(), buf.end()));
 }
 }
 
 
 OptionPtr
 OptionPtr
@@ -662,8 +660,7 @@ OptionDefinition::factoryIAPrefix6(uint16_t type,
 OptionPtr
 OptionPtr
 OptionDefinition::factorySpecialFormatOption(Option::Universe u,
 OptionDefinition::factorySpecialFormatOption(Option::Universe u,
                                              OptionBufferConstIter begin,
                                              OptionBufferConstIter begin,
-                                             OptionBufferConstIter end,
-                                             UnpackOptionsCallback) const {
+                                             OptionBufferConstIter end) const {
     if (u == Option::V6) {
     if (u == Option::V6) {
         if ((getCode() == D6O_IA_NA || getCode() == D6O_IA_PD) &&
         if ((getCode() == D6O_IA_NA || getCode() == D6O_IA_PD) &&
             haveIA6Format()) {
             haveIA6Format()) {

+ 6 - 27
src/lib/dhcp/option_definition.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -382,17 +382,12 @@ public:
     /// @param type option type.
     /// @param type option type.
     /// @param begin beginning of the option buffer.
     /// @param begin beginning of the option buffer.
     /// @param end end of the option buffer.
     /// @param end end of the option buffer.
-    /// @param callback An instance of the function which parses packet options.
-    /// If this is set to non NULL value this function will be used instead of
-    /// @c isc::dhcp::LibDHCP::unpackOptions6 and
-    /// isc::dhcp::LibDHCP::unpackOptions4.
     ///
     ///
     /// @return instance of the DHCP option.
     /// @return instance of the DHCP option.
     /// @throw InvalidOptionValue if data for the option is invalid.
     /// @throw InvalidOptionValue if data for the option is invalid.
     OptionPtr optionFactory(Option::Universe u, uint16_t type,
     OptionPtr optionFactory(Option::Universe u, uint16_t type,
                             OptionBufferConstIter begin,
                             OptionBufferConstIter begin,
-                            OptionBufferConstIter end,
-                            UnpackOptionsCallback callback = NULL) const;
+                            OptionBufferConstIter end) const;
 
 
     /// @brief Option factory.
     /// @brief Option factory.
     ///
     ///
@@ -407,16 +402,11 @@ public:
     /// @param u option universe (V4 or V6).
     /// @param u option universe (V4 or V6).
     /// @param type option type.
     /// @param type option type.
     /// @param buf option buffer.
     /// @param buf option buffer.
-    /// @param callback An instance of the function which parses packet options.
-    /// If this is set to non NULL value this function will be used instead of
-    /// @c isc::dhcp::LibDHCP::unpackOptions6 and
-    /// isc::dhcp::LibDHCP::unpackOptions4.
     ///
     ///
     /// @return instance of the DHCP option.
     /// @return instance of the DHCP option.
     /// @throw InvalidOptionValue if data for the option is invalid.
     /// @throw InvalidOptionValue if data for the option is invalid.
     OptionPtr optionFactory(Option::Universe u, uint16_t type,
     OptionPtr optionFactory(Option::Universe u, uint16_t type,
-                            const OptionBuffer& buf = OptionBuffer(),
-                            UnpackOptionsCallback callback = NULL) const;
+                            const OptionBuffer& buf = OptionBuffer()) const;
 
 
     /// @brief Option factory.
     /// @brief Option factory.
     ///
     ///
@@ -534,10 +524,6 @@ public:
     /// encapsulated option space are sub options of this option.
     /// encapsulated option space are sub options of this option.
     /// @param begin iterator pointing to the beginning of the buffer.
     /// @param begin iterator pointing to the beginning of the buffer.
     /// @param end iterator pointing to the end of the buffer.
     /// @param end iterator pointing to the end of the buffer.
-    /// @param callback An instance of the function which parses packet options.
-    /// If this is set to non NULL value this function will be used instead of
-    /// @c isc::dhcp::LibDHCP::unpackOptions6 and
-    /// isc::dhcp::LibDHCP::unpackOptions4.
     /// @tparam T type of the data field (must be one of the uintX_t or intX_t).
     /// @tparam T type of the data field (must be one of the uintX_t or intX_t).
     ///
     ///
     /// @throw isc::OutOfRange if provided option buffer length is invalid.
     /// @throw isc::OutOfRange if provided option buffer length is invalid.
@@ -545,11 +531,9 @@ public:
     static OptionPtr factoryInteger(Option::Universe u, uint16_t type,
     static OptionPtr factoryInteger(Option::Universe u, uint16_t type,
                                     const std::string& encapsulated_space,
                                     const std::string& encapsulated_space,
                                     OptionBufferConstIter begin,
                                     OptionBufferConstIter begin,
-                                    OptionBufferConstIter end,
-                                    UnpackOptionsCallback callback) {
+                                    OptionBufferConstIter end) {
         OptionPtr option(new OptionInt<T>(u, type, 0));
         OptionPtr option(new OptionInt<T>(u, type, 0));
         option->setEncapsulatedSpace(encapsulated_space);
         option->setEncapsulatedSpace(encapsulated_space);
-        option->setCallback(callback);
         option->unpack(begin, end);
         option->unpack(begin, end);
         return (option);
         return (option);
     }
     }
@@ -586,18 +570,13 @@ private:
     /// @param u A universe (V4 or V6).
     /// @param u A universe (V4 or V6).
     /// @param begin beginning of the option buffer.
     /// @param begin beginning of the option buffer.
     /// @param end end of the option buffer.
     /// @param end end of the option buffer.
-    /// @param callback An instance of the function which parses packet options.
-    /// If this is set to non NULL value this function will be used instead of
-    /// @c isc::dhcp::LibDHCP::unpackOptions6 and
-    /// isc::dhcp::LibDHCP::unpackOptions4.
     ///
     ///
     /// @return An instance of the option having special format or NULL if
     /// @return An instance of the option having special format or NULL if
     /// such an option can't be created because an option with the given
     /// such an option can't be created because an option with the given
     /// option code hasn't got the special format.
     /// option code hasn't got the special format.
     OptionPtr factorySpecialFormatOption(Option::Universe u,
     OptionPtr factorySpecialFormatOption(Option::Universe u,
                                          OptionBufferConstIter begin,
                                          OptionBufferConstIter begin,
-                                         OptionBufferConstIter end,
-                                         UnpackOptionsCallback callback) const;
+                                         OptionBufferConstIter end) const;
 
 
     /// @brief Check if specified option format is a record with 3 fields
     /// @brief Check if specified option format is a record with 3 fields
     /// where first one is custom, and two others are uint32.
     /// where first one is custom, and two others are uint32.
@@ -739,7 +718,7 @@ typedef boost::multi_index_container<
 typedef boost::shared_ptr<OptionDefContainer> OptionDefContainerPtr;
 typedef boost::shared_ptr<OptionDefContainer> OptionDefContainerPtr;
 
 
 /// Container that holds various vendor option containers
 /// Container that holds various vendor option containers
-typedef std::map<uint32_t, OptionDefContainer> VendorOptionDefContainers;
+typedef std::map<uint32_t, OptionDefContainerPtr> VendorOptionDefContainers;
 
 
 /// Type of the index #1 - option type.
 /// Type of the index #1 - option type.
 typedef OptionDefContainer::nth_index<1>::type OptionDefContainerTypeIndex;
 typedef OptionDefContainer::nth_index<1>::type OptionDefContainerTypeIndex;

+ 1 - 12
src/lib/dhcp/pkt.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -284,14 +284,6 @@ public:
     /// that we just received, a copy between those two buffers is necessary.
     /// that we just received, a copy between those two buffers is necessary.
     void repack();
     void repack();
 
 
-    /// @brief Set callback function to be used to parse options.
-    ///
-    /// @param callback An instance of the callback function or NULL to
-    /// uninstall callback.
-    void setCallback(UnpackOptionsCallback callback) {
-        callback_ = callback;
-    }
-
     /// @brief Sets remote IP address.
     /// @brief Sets remote IP address.
     ///
     ///
     /// @param remote specifies remote address
     /// @param remote specifies remote address
@@ -629,9 +621,6 @@ protected:
     // remote HW address (src if receiving packet, dst if sending packet)
     // remote HW address (src if receiving packet, dst if sending packet)
     HWAddrPtr remote_hwaddr_;
     HWAddrPtr remote_hwaddr_;
 
 
-    /// A callback to be called to unpack options from the packet.
-    UnpackOptionsCallback callback_;
-
 private:
 private:
 
 
     /// @brief Generic method that validates and sets HW address.
     /// @brief Generic method that validates and sets HW address.

+ 2 - 10
src/lib/dhcp/pkt4.cc

@@ -201,17 +201,9 @@ Pkt4::unpack() {
 
 
     // Use readVector because a function which parses option requires
     // Use readVector because a function which parses option requires
     // a vector as an input.
     // a vector as an input.
-    size_t offset;
     buffer_in.readVector(opts_buffer, opts_len);
     buffer_in.readVector(opts_buffer, opts_len);
-    if (callback_.empty()) {
-        offset = LibDHCP::unpackOptions4(opts_buffer, "dhcp4", options_);
-    } else {
-        // The last two arguments are set to NULL because they are
-        // specific to DHCPv6 options parsing. They are unused for
-        // DHCPv4 case. In DHCPv6 case they hold are the relay message
-        // offset and length.
-        offset = callback_(opts_buffer, "dhcp4", options_, NULL, NULL);
-    }
+
+    size_t offset = LibDHCP::unpackOptions4(opts_buffer, "dhcp4", options_);
 
 
     // If offset is not equal to the size and there is no DHO_END,
     // If offset is not equal to the size and there is no DHO_END,
     // then something is wrong here. We either parsed past input
     // then something is wrong here. We either parsed past input

+ 3 - 16
src/lib/dhcp/pkt6.cc

@@ -336,15 +336,7 @@ Pkt6::unpackMsg(OptionBuffer::const_iterator begin,
 
 
     // If custom option parsing function has been set, use this function
     // If custom option parsing function has been set, use this function
     // to parse options. Otherwise, use standard function from libdhcp.
     // to parse options. Otherwise, use standard function from libdhcp.
-    size_t offset;
-    if (callback_.empty()) {
-        offset = LibDHCP::unpackOptions6(opt_buffer, "dhcp6", options_);
-    } else {
-        // The last two arguments hold the DHCPv6 Relay message offset and
-        // length. Setting them to NULL because we are dealing with the
-        // not-relayed message.
-        offset = callback_(opt_buffer, "dhcp6", options_, NULL, NULL);
-    }
+    size_t offset = LibDHCP::unpackOptions6(opt_buffer, "dhcp6", options_);
 
 
     // If offset is not equal to the size, then something is wrong here. We
     // If offset is not equal to the size, then something is wrong here. We
     // either parsed past input buffer (bug in our code) or we haven't parsed
     // either parsed past input buffer (bug in our code) or we haven't parsed
@@ -394,13 +386,8 @@ Pkt6::unpackRelayMsg() {
 
 
         // If custom option parsing function has been set, use this function
         // If custom option parsing function has been set, use this function
         // to parse options. Otherwise, use standard function from libdhcp.
         // to parse options. Otherwise, use standard function from libdhcp.
-        if (callback_.empty()) {
-            LibDHCP::unpackOptions6(opt_buffer, "dhcp6", relay.options_,
-                                    &relay_msg_offset, &relay_msg_len);
-        } else {
-            callback_(opt_buffer, "dhcp6", relay.options_,
-                      &relay_msg_offset, &relay_msg_len);
-        }
+        LibDHCP::unpackOptions6(opt_buffer, "dhcp6", relay.options_,
+                                &relay_msg_offset, &relay_msg_len);
 
 
         /// @todo: check that each option appears at most once
         /// @todo: check that each option appears at most once
         //relay.interface_id_ = options->getOption(D6O_INTERFACE_ID);
         //relay.interface_id_ = options->getOption(D6O_INTERFACE_ID);

+ 318 - 32
src/lib/dhcp/tests/libdhcp++_unittest.cc

@@ -6,6 +6,7 @@
 
 
 #include <config.h>
 #include <config.h>
 
 
+#include <asiolink/io_address.h>
 #include <dhcp/dhcp4.h>
 #include <dhcp/dhcp4.h>
 #include <dhcp/dhcp6.h>
 #include <dhcp/dhcp6.h>
 #include <dhcp/docsis3_option_defs.h>
 #include <dhcp/docsis3_option_defs.h>
@@ -22,6 +23,7 @@
 #include <dhcp/option_int.h>
 #include <dhcp/option_int.h>
 #include <dhcp/option_int_array.h>
 #include <dhcp/option_int_array.h>
 #include <dhcp/option_opaque_data_tuples.h>
 #include <dhcp/option_opaque_data_tuples.h>
+#include <dhcp/option_space.h>
 #include <dhcp/option_string.h>
 #include <dhcp/option_string.h>
 #include <dhcp/option_vendor.h>
 #include <dhcp/option_vendor.h>
 #include <dhcp/option_vendor_class.h>
 #include <dhcp/option_vendor_class.h>
@@ -39,6 +41,7 @@
 
 
 using namespace std;
 using namespace std;
 using namespace isc;
 using namespace isc;
+using namespace isc::asiolink;
 using namespace isc::dhcp;
 using namespace isc::dhcp;
 using namespace isc::util;
 using namespace isc::util;
 
 
@@ -51,7 +54,19 @@ const uint16_t OPTION_CM_MAC = 1026;
 
 
 class LibDhcpTest : public ::testing::Test {
 class LibDhcpTest : public ::testing::Test {
 public:
 public:
-    LibDhcpTest() { }
+    /// @brief Constructor.
+    ///
+    /// Removes runtime option definitions.
+    LibDhcpTest() {
+        LibDHCP::clearRuntimeOptionDefs();
+    }
+
+    /// @brief Destructor.
+    ///
+    /// Removes runtime option definitions.
+    virtual ~LibDhcpTest() {
+        LibDHCP::clearRuntimeOptionDefs();
+    }
 
 
     /// @brief Generic factory function to create any option.
     /// @brief Generic factory function to create any option.
     ///
     ///
@@ -219,10 +234,10 @@ private:
         // the definition for a particular option code.
         // the definition for a particular option code.
         // We don't have to initialize option definitions here because they
         // We don't have to initialize option definitions here because they
         // are initialized in the class's constructor.
         // are initialized in the class's constructor.
-        OptionDefContainer options = LibDHCP::getOptionDefs(u);
+        OptionDefContainerPtr options = LibDHCP::getOptionDefs(u);
         // Get the container index #1. This one allows for searching
         // Get the container index #1. This one allows for searching
         // option definitions using option code.
         // option definitions using option code.
-        const OptionDefContainerTypeIndex& idx = options.get<1>();
+        const OptionDefContainerTypeIndex& idx = options->get<1>();
         // Get 'all' option definitions for a particular option code.
         // Get 'all' option definitions for a particular option code.
         // For standard options we expect that the range returned
         // For standard options we expect that the range returned
         // will contain single option as their codes are unique.
         // will contain single option as their codes are unique.
@@ -504,22 +519,133 @@ TEST_F(LibDhcpTest, unpackOptions6) {
     EXPECT_TRUE(x == options.end()); // option 32000 not found */
     EXPECT_TRUE(x == options.end()); // option 32000 not found */
 }
 }
 
 
+// Check parsing of an empty DHCPv6 option.
+TEST_F(LibDhcpTest, unpackEmptyOption6) {
+    // Create option definition for the option code 1024 without fields.
+    OptionDefinitionPtr opt_def(new OptionDefinition("option-empty", 1024,
+                                                     "empty", false));
+
+    // Use it as runtime option definition within standard options space.
+    // The tested code should find this option definition within runtime
+    // option definitions set when it detects that this definition is
+    // not a standard definition.
+    OptionDefSpaceContainer defs;
+    defs.addItem(opt_def, DHCP6_OPTION_SPACE);
+    LibDHCP::setRuntimeOptionDefs(defs);
+    LibDHCP::commitRuntimeOptionDefs();
+
+    // Create the buffer holding the structure of the empty option.
+    const uint8_t raw_data[] = {
+      0x04, 0x00,                // option code = 1024
+      0x00, 0x00                 // option length = 0
+    };
+    size_t raw_data_len = sizeof(raw_data) / sizeof(uint8_t);
+    OptionBuffer buf(raw_data, raw_data + raw_data_len);
+
+    // Parse options.
+    OptionCollection options;
+    ASSERT_NO_THROW(LibDHCP::unpackOptions6(buf, DHCP6_OPTION_SPACE,
+                                            options));
+
+    // There should be one option.
+    ASSERT_EQ(1, options.size());
+    OptionPtr option_empty = options.begin()->second;
+    ASSERT_TRUE(option_empty);
+    EXPECT_EQ(1024, option_empty->getType());
+    EXPECT_EQ(4, option_empty->len());
+}
+
+// This test verifies that the following option structure can be parsed:
+// - option (option space 'foobar')
+//   - sub option (option space 'foo')
+//      - sub option (option space 'bar')
+TEST_F(LibDhcpTest, unpackSubOptions6) {
+    // Create option definition for each level of encapsulation. Each option
+    // definition is for the option code 1. Options may have the same
+    // option code because they belong to different option spaces.
+
+    // Top level option encapsulates options which belong to 'space-foo'.
+    OptionDefinitionPtr opt_def(new OptionDefinition("option-foobar", 1, "uint32",
+                                                      "space-foo"));\
+    // Middle option encapsulates options which belong to 'space-bar'
+    OptionDefinitionPtr opt_def2(new OptionDefinition("option-foo", 1, "uint16",
+                                                      "space-bar"));
+    // Low level option doesn't encapsulate any option space.
+    OptionDefinitionPtr opt_def3(new OptionDefinition("option-bar", 1,
+                                                      "uint8"));
+
+    // Register created option definitions as runtime option definitions.
+    OptionDefSpaceContainer defs;
+    ASSERT_NO_THROW(defs.addItem(opt_def, "space-foobar"));
+    ASSERT_NO_THROW(defs.addItem(opt_def2, "space-foo"));
+    ASSERT_NO_THROW(defs.addItem(opt_def3, "space-bar"));
+    LibDHCP::setRuntimeOptionDefs(defs);
+    LibDHCP::commitRuntimeOptionDefs();
+
+    // Create the buffer holding the structure of options.
+    const char raw_data[] = {
+        // First option starts here.
+        0x00, 0x01,   // option code = 1
+        0x00, 0x0F,   // option length = 15
+        0x00, 0x01, 0x02, 0x03, // This option carries uint32 value
+        // Sub option starts here.
+        0x00, 0x01,  // option code = 1
+        0x00, 0x07,  // option length = 7
+        0x01, 0x02,  // this option carries uint16 value
+        // Last option starts here.
+        0x00, 0x01,  // option code = 1
+        0x00, 0x01,  // option length = 1
+        0x00 // This option carries a single uint8 value and has no sub options.
+    };
+    OptionBuffer buf(raw_data, raw_data + sizeof(raw_data));
+
+    // Parse options.
+    OptionCollection options;
+    ASSERT_NO_THROW(LibDHCP::unpackOptions6(buf, "space-foobar", options, 0, 0));
+
+    // There should be one top level option.
+    ASSERT_EQ(1, options.size());
+    boost::shared_ptr<OptionInt<uint32_t> > option_foobar =
+        boost::dynamic_pointer_cast<OptionInt<uint32_t> >(options.begin()->
+                                                          second);
+    ASSERT_TRUE(option_foobar);
+    EXPECT_EQ(1, option_foobar->getType());
+    EXPECT_EQ(0x00010203, option_foobar->getValue());
+    // There should be a middle level option held in option_foobar.
+    boost::shared_ptr<OptionInt<uint16_t> > option_foo =
+        boost::dynamic_pointer_cast<OptionInt<uint16_t> >(option_foobar->
+                                                          getOption(1));
+    ASSERT_TRUE(option_foo);
+    EXPECT_EQ(1, option_foo->getType());
+    EXPECT_EQ(0x0102, option_foo->getValue());
+    // Finally, there should be a low level option under option_foo.
+    boost::shared_ptr<OptionInt<uint8_t> > option_bar =
+        boost::dynamic_pointer_cast<OptionInt<uint8_t> >(option_foo->getOption(1));
+    ASSERT_TRUE(option_bar);
+    EXPECT_EQ(1, option_bar->getType());
+    EXPECT_EQ(0x0, option_bar->getValue());
+}
+
 /// V4 Options being used to test pack/unpack operations.
 /// V4 Options being used to test pack/unpack operations.
 /// These are variable length options only so as there
 /// These are variable length options only so as there
 /// is no restriction on the data length being carried by them.
 /// is no restriction on the data length being carried by them.
 /// For simplicity, we assign data of the length 3 for each
 /// For simplicity, we assign data of the length 3 for each
 /// of them.
 /// of them.
 static uint8_t v4_opts[] = {
 static uint8_t v4_opts[] = {
-    12,  3, 0,   1,  2, // Hostname
-    60,  3, 10, 11, 12, // Class Id
-    14,  3, 20, 21, 22, // Merit Dump File
-    254, 3, 30, 31, 32, // Reserved
-    128, 3, 40, 41, 42, // Vendor specific
-    0x52, 0x19,         // RAI
+    12,  3, 0,   1,  2,        // Hostname
+    60,  3, 10, 11, 12,        // Class Id
+    14,  3, 20, 21, 22,        // Merit Dump File
+    254, 3, 30, 31, 32,        // Reserved
+    128, 3, 40, 41, 42,        // Vendor specific
+    125, 11, 0, 0, 0x11, 0x8B, // V-I Vendor-Specific Information (Cable Labs)
+    6, 2, 4, 10, 0, 0, 10,     // TFTP servers suboption (2)
+    43, 2,                     // Vendor Specific Information
+    0xDC, 0,                   // VSI suboption
+    0x52, 0x19,                // RAI
     0x01, 0x04, 0x20, 0x00, 0x00, 0x02, // Agent Circuit ID
     0x01, 0x04, 0x20, 0x00, 0x00, 0x02, // Agent Circuit ID
     0x02, 0x06, 0x20, 0xE5, 0x2A, 0xB8, 0x15, 0x14, // Agent Remote ID
     0x02, 0x06, 0x20, 0xE5, 0x2A, 0xB8, 0x15, 0x14, // Agent Remote ID
     0x09, 0x09, 0x00, 0x00, 0x11, 0x8B, 0x04, // Vendor Specific Information
     0x09, 0x09, 0x00, 0x00, 0x11, 0x8B, 0x04, // Vendor Specific Information
-    0x01, 0x02, 0x03, 0x00  // Vendor Specific Information continued
+    0x01, 0x02, 0x03, 0x00 // Vendor Specific Information continued
 };
 };
 
 
 // This test verifies that pack options for v4 is working correctly.
 // This test verifies that pack options for v4 is working correctly.
@@ -539,6 +665,15 @@ TEST_F(LibDhcpTest, packOptions4) {
     OptionPtr opt4(new Option(Option::V4,254, payload[3]));
     OptionPtr opt4(new Option(Option::V4,254, payload[3]));
     OptionPtr opt5(new Option(Option::V4,128, payload[4]));
     OptionPtr opt5(new Option(Option::V4,128, payload[4]));
 
 
+    // Create vendor option instance with DOCSIS3.0 enterprise id.
+    OptionVendorPtr vivsi(new OptionVendor(Option::V4, 4491));
+    vivsi->addOption(OptionPtr(new Option4AddrLst(DOCSIS3_V4_TFTP_SERVERS,
+                                                  IOAddress("10.0.0.10"))));
+
+    OptionPtr vsi(new Option(Option::V4, DHO_VENDOR_ENCAPSULATED_OPTIONS,
+                              OptionBuffer()));
+    vsi->addOption(OptionPtr(new Option(Option::V4, 0xDC, OptionBuffer())));
+
     // Add RAI option, which comprises 3 sub-options.
     // Add RAI option, which comprises 3 sub-options.
 
 
     // Get the option definition for RAI option. This option is represented
     // Get the option definition for RAI option. This option is represented
@@ -556,19 +691,19 @@ TEST_F(LibDhcpTest, packOptions4) {
 
 
     // Create Ciruit ID sub-option and add to RAI.
     // Create Ciruit ID sub-option and add to RAI.
     OptionPtr circuit_id(new Option(Option::V4, RAI_OPTION_AGENT_CIRCUIT_ID,
     OptionPtr circuit_id(new Option(Option::V4, RAI_OPTION_AGENT_CIRCUIT_ID,
-                                    OptionBuffer(v4_opts + 29,
-                                                 v4_opts + 33)));
+                                    OptionBuffer(v4_opts + 46,
+                                                 v4_opts + 50)));
     rai->addOption(circuit_id);
     rai->addOption(circuit_id);
 
 
     // Create Remote ID option and add to RAI.
     // Create Remote ID option and add to RAI.
     OptionPtr remote_id(new Option(Option::V4, RAI_OPTION_REMOTE_ID,
     OptionPtr remote_id(new Option(Option::V4, RAI_OPTION_REMOTE_ID,
-                                   OptionBuffer(v4_opts + 35, v4_opts + 41)));
+                                   OptionBuffer(v4_opts + 52, v4_opts + 58)));
     rai->addOption(remote_id);
     rai->addOption(remote_id);
 
 
     // Create Vendor Specific Information and add to RAI.
     // Create Vendor Specific Information and add to RAI.
-    OptionPtr vsi(new Option(Option::V4, RAI_OPTION_VSI,
-                             OptionBuffer(v4_opts + 43, v4_opts + 52)));
-    rai->addOption(vsi);
+    OptionPtr rai_vsi(new Option(Option::V4, RAI_OPTION_VSI,
+                                 OptionBuffer(v4_opts + 60, v4_opts + 69)));
+    rai->addOption(rai_vsi);
 
 
     isc::dhcp::OptionCollection opts; // list of options
     isc::dhcp::OptionCollection opts; // list of options
     // Note that we insert each option under the same option code into
     // Note that we insert each option under the same option code into
@@ -580,6 +715,8 @@ TEST_F(LibDhcpTest, packOptions4) {
     opts.insert(make_pair(opt1->getType(), opt3));
     opts.insert(make_pair(opt1->getType(), opt3));
     opts.insert(make_pair(opt1->getType(), opt4));
     opts.insert(make_pair(opt1->getType(), opt4));
     opts.insert(make_pair(opt1->getType(), opt5));
     opts.insert(make_pair(opt1->getType(), opt5));
+    opts.insert(make_pair(opt1->getType(), vivsi));
+    opts.insert(make_pair(opt1->getType(), vsi));
     opts.insert(make_pair(opt1->getType(), rai));
     opts.insert(make_pair(opt1->getType(), rai));
 
 
     OutputBuffer buf(100);
     OutputBuffer buf(100);
@@ -659,19 +796,58 @@ TEST_F(LibDhcpTest, unpackOptions4) {
     EXPECT_EQ(0, memcmp(&option14->getValue()[0], v4_opts + 12, 3)); // data len=3
     EXPECT_EQ(0, memcmp(&option14->getValue()[0], v4_opts + 12, 3)); // data len=3
 
 
     x = options.find(254);
     x = options.find(254);
-    ASSERT_FALSE(x == options.end()); // option 3 should exist
+    ASSERT_FALSE(x == options.end()); // option 4 should exist
     EXPECT_EQ(254, x->second->getType());  // this should be option 254
     EXPECT_EQ(254, x->second->getType());  // this should be option 254
     ASSERT_EQ(3, x->second->getData().size()); // it should be of length 3
     ASSERT_EQ(3, x->second->getData().size()); // it should be of length 3
     EXPECT_EQ(5, x->second->len()); // total option length 5
     EXPECT_EQ(5, x->second->len()); // total option length 5
     EXPECT_EQ(0, memcmp(&x->second->getData()[0], v4_opts + 17, 3)); // data len=3
     EXPECT_EQ(0, memcmp(&x->second->getData()[0], v4_opts + 17, 3)); // data len=3
 
 
     x = options.find(128);
     x = options.find(128);
-    ASSERT_FALSE(x == options.end()); // option 3 should exist
-    EXPECT_EQ(128, x->second->getType());  // this should be option 254
+    ASSERT_FALSE(x == options.end()); // option 5 should exist
+    EXPECT_EQ(128, x->second->getType());  // this should be option 128
     ASSERT_EQ(3, x->second->getData().size()); // it should be of length 3
     ASSERT_EQ(3, x->second->getData().size()); // it should be of length 3
     EXPECT_EQ(5, x->second->len()); // total option length 5
     EXPECT_EQ(5, x->second->len()); // total option length 5
     EXPECT_EQ(0, memcmp(&x->second->getData()[0], v4_opts + 22, 3)); // data len=3
     EXPECT_EQ(0, memcmp(&x->second->getData()[0], v4_opts + 22, 3)); // data len=3
 
 
+    // Verify that V-I Vendor Specific Information option is parsed correctly.
+    x = options.find(125);
+    ASSERT_FALSE(x == options.end());
+    OptionVendorPtr vivsi = boost::dynamic_pointer_cast<OptionVendor>(x->second);
+    ASSERT_TRUE(vivsi);
+    EXPECT_EQ(DHO_VIVSO_SUBOPTIONS, vivsi->getType());
+    EXPECT_EQ(4491, vivsi->getVendorId());
+    OptionCollection suboptions = vivsi->getOptions();
+
+    // There should be one suboption of V-I VSI.
+    ASSERT_EQ(1, suboptions.size());
+    // This vendor option has a standard definition and thus should be
+    // converted to appropriate class, i.e. Option4AddrLst. If this cast
+    // fails, it means that its definition was not used while it was
+    // parsed.
+    Option4AddrLstPtr tftp =
+        boost::dynamic_pointer_cast<Option4AddrLst>(suboptions.begin()->second);
+    ASSERT_TRUE(tftp);
+    EXPECT_EQ(DOCSIS3_V4_TFTP_SERVERS, tftp->getType());
+    EXPECT_EQ(6, tftp->len());
+    Option4AddrLst::AddressContainer addresses = tftp->getAddresses();
+    ASSERT_EQ(1, addresses.size());
+    EXPECT_EQ("10.0.0.10", addresses[0].toText());
+
+    // Vendor Specific Information option
+    x = options.find(43);
+    ASSERT_FALSE(x == options.end());
+    OptionPtr vsi = x->second;
+    ASSERT_TRUE(vsi);
+    EXPECT_EQ(DHO_VENDOR_ENCAPSULATED_OPTIONS, vsi->getType());
+    suboptions = vsi->getOptions();
+
+    // There should be one suboption of VSI.
+    ASSERT_EQ(1, suboptions.size());
+    OptionPtr eso = suboptions.begin()->second;
+    ASSERT_TRUE(eso);
+    EXPECT_EQ(0xdc, eso->getType());
+    EXPECT_EQ(2, eso->len());
+
     // Checking DHCP Relay Agent Information Option.
     // Checking DHCP Relay Agent Information Option.
     x = options.find(DHO_DHCP_AGENT_OPTIONS);
     x = options.find(DHO_DHCP_AGENT_OPTIONS);
     ASSERT_FALSE(x == options.end());
     ASSERT_FALSE(x == options.end());
@@ -694,21 +870,21 @@ TEST_F(LibDhcpTest, unpackOptions4) {
     ASSERT_TRUE(rai_option);
     ASSERT_TRUE(rai_option);
     EXPECT_EQ(RAI_OPTION_AGENT_CIRCUIT_ID, rai_option->getType());
     EXPECT_EQ(RAI_OPTION_AGENT_CIRCUIT_ID, rai_option->getType());
     ASSERT_EQ(6, rai_option->len());
     ASSERT_EQ(6, rai_option->len());
-    EXPECT_EQ(0, memcmp(&rai_option->getData()[0], v4_opts + 29, 4));
+    EXPECT_EQ(0, memcmp(&rai_option->getData()[0], v4_opts + 46, 4));
 
 
     // Check that Remote ID option is among parsed options.
     // Check that Remote ID option is among parsed options.
     rai_option = rai->getOption(RAI_OPTION_REMOTE_ID);
     rai_option = rai->getOption(RAI_OPTION_REMOTE_ID);
     ASSERT_TRUE(rai_option);
     ASSERT_TRUE(rai_option);
     EXPECT_EQ(RAI_OPTION_REMOTE_ID, rai_option->getType());
     EXPECT_EQ(RAI_OPTION_REMOTE_ID, rai_option->getType());
     ASSERT_EQ(8, rai_option->len());
     ASSERT_EQ(8, rai_option->len());
-    EXPECT_EQ(0, memcmp(&rai_option->getData()[0], v4_opts + 35, 6));
+    EXPECT_EQ(0, memcmp(&rai_option->getData()[0], v4_opts + 52, 6));
 
 
     // Check that Vendor Specific Information option is among parsed options.
     // Check that Vendor Specific Information option is among parsed options.
     rai_option = rai->getOption(RAI_OPTION_VSI);
     rai_option = rai->getOption(RAI_OPTION_VSI);
     ASSERT_TRUE(rai_option);
     ASSERT_TRUE(rai_option);
     EXPECT_EQ(RAI_OPTION_VSI, rai_option->getType());
     EXPECT_EQ(RAI_OPTION_VSI, rai_option->getType());
     ASSERT_EQ(11, rai_option->len());
     ASSERT_EQ(11, rai_option->len());
-    EXPECT_EQ(0, memcmp(&rai_option->getData()[0], v4_opts + 43, 9));
+    EXPECT_EQ(0, memcmp(&rai_option->getData()[0], v4_opts + 60, 9));
 
 
     // Make sure, that option other than those above is not present.
     // Make sure, that option other than those above is not present.
     EXPECT_FALSE(rai->getOption(10));
     EXPECT_FALSE(rai->getOption(10));
@@ -725,6 +901,116 @@ TEST_F(LibDhcpTest, unpackOptions4) {
 
 
 }
 }
 
 
+// Check parsing of an empty option.
+TEST_F(LibDhcpTest, unpackEmptyOption4) {
+    // Create option definition for the option code 254 without fields.
+    OptionDefinitionPtr opt_def(new OptionDefinition("option-empty", 254,
+                                                     "empty", false));
+
+    // Use it as runtime option definition within standard options space.
+    // The tested code should find this option definition within runtime
+    // option definitions set when it detects that this definition is
+    // not a standard definition.
+    OptionDefSpaceContainer defs;
+    defs.addItem(opt_def, DHCP4_OPTION_SPACE);
+    LibDHCP::setRuntimeOptionDefs(defs);
+    LibDHCP::commitRuntimeOptionDefs();
+
+    // Create the buffer holding the structure of the empty option.
+    const uint8_t raw_data[] = {
+      0xFE,                     // option code = 254
+      0x00                      // option length = 0
+    };
+    size_t raw_data_len = sizeof(raw_data) / sizeof(uint8_t);
+    OptionBuffer buf(raw_data, raw_data + raw_data_len);
+
+    // Parse options.
+    OptionCollection options;
+    ASSERT_NO_THROW(LibDHCP::unpackOptions4(buf, DHCP4_OPTION_SPACE,
+                                            options));
+
+    // There should be one option.
+    ASSERT_EQ(1, options.size());
+    OptionPtr option_empty = options.begin()->second;
+    ASSERT_TRUE(option_empty);
+    EXPECT_EQ(254, option_empty->getType());
+    EXPECT_EQ(2, option_empty->len());
+}
+
+// This test verifies that the following option structure can be parsed:
+// - option (option space 'foobar')
+//   - sub option (option space 'foo')
+//      - sub option (option space 'bar')
+// @todo Add more thorough unit tests for unpackOptions.
+TEST_F(LibDhcpTest, unpackSubOptions4) {
+    // Create option definition for each level of encapsulation. Each option
+    // definition is for the option code 1. Options may have the same
+    // option code because they belong to different option spaces.
+
+    // Top level option encapsulates options which belong to 'space-foo'.
+    OptionDefinitionPtr opt_def(new OptionDefinition("option-foobar", 1, "uint32",
+                                                      "space-foo"));\
+    // Middle option encapsulates options which belong to 'space-bar'
+    OptionDefinitionPtr opt_def2(new OptionDefinition("option-foo", 1, "uint16",
+                                                      "space-bar"));
+    // Low level option doesn't encapsulate any option space.
+    OptionDefinitionPtr opt_def3(new OptionDefinition("option-bar", 1,
+                                                      "uint8"));
+
+    // Register created option definitions as runtime option definitions.
+    OptionDefSpaceContainer defs;
+    ASSERT_NO_THROW(defs.addItem(opt_def, "space-foobar"));
+    ASSERT_NO_THROW(defs.addItem(opt_def2, "space-foo"));
+    ASSERT_NO_THROW(defs.addItem(opt_def3, "space-bar"));
+    LibDHCP::setRuntimeOptionDefs(defs);
+    LibDHCP::commitRuntimeOptionDefs();
+
+    // Create the buffer holding the structure of options.
+    const uint8_t raw_data[] = {
+        // First option starts here.
+        0x01,                   // option code = 1
+        0x0B,                   // option length = 11
+        0x00, 0x01, 0x02, 0x03, // This option carries uint32 value
+        // Sub option starts here.
+        0x01,                   // option code = 1
+        0x05,                   // option length = 5
+        0x01, 0x02,             // this option carries uint16 value
+        // Last option starts here.
+        0x01,                   // option code = 1
+        0x01,                   // option length = 1
+        0x00                    // This option carries a single uint8
+                                // value and has no sub options.
+    };
+    size_t raw_data_len = sizeof(raw_data) / sizeof(uint8_t);
+    OptionBuffer buf(raw_data, raw_data + raw_data_len);
+
+    // Parse options.
+    OptionCollection options;
+    ASSERT_NO_THROW(LibDHCP::unpackOptions4(buf, "space-foobar", options));
+
+    // There should be one top level option.
+    ASSERT_EQ(1, options.size());
+    boost::shared_ptr<OptionInt<uint32_t> > option_foobar =
+        boost::dynamic_pointer_cast<OptionInt<uint32_t> >(options.begin()->
+                                                          second);
+    ASSERT_TRUE(option_foobar);
+    EXPECT_EQ(1, option_foobar->getType());
+    EXPECT_EQ(0x00010203, option_foobar->getValue());
+    // There should be a middle level option held in option_foobar.
+    boost::shared_ptr<OptionInt<uint16_t> > option_foo =
+        boost::dynamic_pointer_cast<OptionInt<uint16_t> >(option_foobar->
+                                                          getOption(1));
+    ASSERT_TRUE(option_foo);
+    EXPECT_EQ(1, option_foo->getType());
+    EXPECT_EQ(0x0102, option_foo->getValue());
+    // Finally, there should be a low level option under option_foo.
+    boost::shared_ptr<OptionInt<uint8_t> > option_bar =
+        boost::dynamic_pointer_cast<OptionInt<uint8_t> >(option_foo->getOption(1));
+    ASSERT_TRUE(option_bar);
+    EXPECT_EQ(1, option_bar->getType());
+    EXPECT_EQ(0x0, option_bar->getValue());
+}
+
 TEST_F(LibDhcpTest, isStandardOption4) {
 TEST_F(LibDhcpTest, isStandardOption4) {
     // Get all option codes that are not occupied by standard options.
     // Get all option codes that are not occupied by standard options.
     const uint16_t unassigned_codes[] = { 84, 96, 102, 103, 104, 105, 106, 107, 108,
     const uint16_t unassigned_codes[] = { 84, 96, 102, 103, 104, 105, 106, 107, 108,
@@ -1338,10 +1624,10 @@ TEST_F(LibDhcpTest, stdOptionDefs6) {
 // an option name.
 // an option name.
 TEST_F(LibDhcpTest, getOptionDefByName6) {
 TEST_F(LibDhcpTest, getOptionDefByName6) {
     // Get all definitions.
     // Get all definitions.
-    const OptionDefContainer& defs = LibDHCP::getOptionDefs(Option::V6);
+    const OptionDefContainerPtr defs = LibDHCP::getOptionDefs(Option::V6);
     // For each definition try to find it using option name.
     // For each definition try to find it using option name.
-    for (OptionDefContainer::const_iterator def = defs.begin();
-         def != defs.end(); ++def) {
+    for (OptionDefContainer::const_iterator def = defs->begin();
+         def != defs->end(); ++def) {
         OptionDefinitionPtr def_by_name =
         OptionDefinitionPtr def_by_name =
             LibDHCP::getOptionDef(Option::V6, (*def)->getName());
             LibDHCP::getOptionDef(Option::V6, (*def)->getName());
         ASSERT_TRUE(def_by_name);
         ASSERT_TRUE(def_by_name);
@@ -1354,10 +1640,10 @@ TEST_F(LibDhcpTest, getOptionDefByName6) {
 // an option name.
 // an option name.
 TEST_F(LibDhcpTest, getOptionDefByName4) {
 TEST_F(LibDhcpTest, getOptionDefByName4) {
     // Get all definitions.
     // Get all definitions.
-    const OptionDefContainer& defs = LibDHCP::getOptionDefs(Option::V4);
+    const OptionDefContainerPtr defs = LibDHCP::getOptionDefs(Option::V4);
     // For each definition try to find it using option name.
     // For each definition try to find it using option name.
-    for (OptionDefContainer::const_iterator def = defs.begin();
-         def != defs.end(); ++def) {
+    for (OptionDefContainer::const_iterator def = defs->begin();
+         def != defs->end(); ++def) {
         OptionDefinitionPtr def_by_name =
         OptionDefinitionPtr def_by_name =
             LibDHCP::getOptionDef(Option::V4, (*def)->getName());
             LibDHCP::getOptionDef(Option::V4, (*def)->getName());
         ASSERT_TRUE(def_by_name);
         ASSERT_TRUE(def_by_name);
@@ -1368,9 +1654,9 @@ TEST_F(LibDhcpTest, getOptionDefByName4) {
 // This test checks if the definition of the DHCPv6 vendor option can
 // This test checks if the definition of the DHCPv6 vendor option can
 // be searched by option name.
 // be searched by option name.
 TEST_F(LibDhcpTest, getVendorOptionDefByName6) {
 TEST_F(LibDhcpTest, getVendorOptionDefByName6) {
-    const OptionDefContainer* defs =
+    const OptionDefContainerPtr& defs =
         LibDHCP::getVendorOption6Defs(VENDOR_ID_CABLE_LABS);
         LibDHCP::getVendorOption6Defs(VENDOR_ID_CABLE_LABS);
-    ASSERT_TRUE(defs != NULL);
+    ASSERT_TRUE(defs);
     for (OptionDefContainer::const_iterator def = defs->begin();
     for (OptionDefContainer::const_iterator def = defs->begin();
          def != defs->end(); ++def) {
          def != defs->end(); ++def) {
         OptionDefinitionPtr def_by_name =
         OptionDefinitionPtr def_by_name =
@@ -1384,9 +1670,9 @@ TEST_F(LibDhcpTest, getVendorOptionDefByName6) {
 // This test checks if the definition of the DHCPv4 vendor option can
 // This test checks if the definition of the DHCPv4 vendor option can
 // be searched by option name.
 // be searched by option name.
 TEST_F(LibDhcpTest, getVendorOptionDefByName4) {
 TEST_F(LibDhcpTest, getVendorOptionDefByName4) {
-    const OptionDefContainer* defs =
+    const OptionDefContainerPtr& defs =
         LibDHCP::getVendorOption4Defs(VENDOR_ID_CABLE_LABS);
         LibDHCP::getVendorOption4Defs(VENDOR_ID_CABLE_LABS);
-    ASSERT_TRUE(defs != NULL);
+    ASSERT_TRUE(defs);
     for (OptionDefContainer::const_iterator def = defs->begin();
     for (OptionDefContainer::const_iterator def = defs->begin();
          def != defs->end(); ++def) {
          def != defs->end(); ++def) {
         OptionDefinitionPtr def_by_name =
         OptionDefinitionPtr def_by_name =

+ 2 - 1
src/lib/dhcp/tests/option_custom_unittest.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -170,6 +170,7 @@ TEST_F(OptionCustomTest, emptyData) {
         option.reset(new OptionCustom(opt_def, Option::V4, buf.begin(),
         option.reset(new OptionCustom(opt_def, Option::V4, buf.begin(),
                                       buf.end()));
                                       buf.end()));
     );
     );
+
     ASSERT_TRUE(option);
     ASSERT_TRUE(option);
 
 
     // Option is 'empty' so no data fields are expected.
     // Option is 'empty' so no data fields are expected.

+ 1 - 1
src/lib/dhcp/tests/option_definition_unittest.cc

@@ -1242,7 +1242,7 @@ TEST_F(OptionDefinitionTest, integerInvalidType) {
     OptionBuffer buf(1);
     OptionBuffer buf(1);
     EXPECT_THROW(
     EXPECT_THROW(
         OptionDefinition::factoryInteger<bool>(Option::V6, D6O_PREFERENCE, "dhcp6",
         OptionDefinition::factoryInteger<bool>(Option::V6, D6O_PREFERENCE, "dhcp6",
-                                               buf.begin(), buf.end(), NULL),
+                                               buf.begin(), buf.end()),
         isc::dhcp::InvalidDataType
         isc::dhcp::InvalidDataType
     );
     );
 }
 }

+ 1 - 101
src/lib/dhcp/tests/option_unittest.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2011-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -30,55 +30,6 @@ using boost::scoped_ptr;
 
 
 namespace {
 namespace {
 
 
-/// @brief A class which contains a custom callback function to unpack options.
-///
-/// This is a class used by the tests which verify that the custom callback
-/// functions can be installed to unpack options from a message. When the
-/// callback function is called, the executed_ member is set to true to allow
-/// verification that the callback was really called. Internally, this class
-/// uses libdhcp++ to unpack options so the options parsing algorithm remains
-/// unchanged after installation of the callback.
-class CustomUnpackCallback {
-public:
-
-    /// @brief Constructor
-    ///
-    /// Marks that callback hasn't been called.
-    CustomUnpackCallback()
-        : executed_(false) {
-    }
-
-    /// @brief A callback
-    ///
-    /// Contains custom implementation of the callback.
-    ///
-    /// @param buf a A buffer holding options in on-wire format.
-    /// @param option_space A name of the option space being encapsulated by
-    /// the option being parsed.
-    /// @param [out] options A reference to the collection where parsed options
-    /// will be stored.
-    /// @param relay_msg_offset Reference to a size_t structure. If specified,
-    /// offset to beginning of relay_msg option will be stored in it.
-    /// @param relay_msg_len reference to a size_t structure. If specified,
-    /// length of the relay_msg option will be stored in it.
-    /// @return An offset to the first byte after last parsed option.
-    size_t execute(const OptionBuffer& buf,
-                   const std::string& option_space,
-                   isc::dhcp::OptionCollection& options,
-                   size_t* relay_msg_offset,
-                   size_t* relay_msg_len) {
-        // Set the executed_ member to true to allow verification that the
-        // callback has been actually called.
-        executed_ = true;
-        // Use default implementation of the unpack algorithm to parse options.
-        return (LibDHCP::unpackOptions6(buf, option_space, options, relay_msg_offset,
-                                        relay_msg_len));
-    }
-
-    /// A flag which indicates if callback function has been called.
-    bool executed_;
-};
-
 /// @brief A class which derives from option and exposes protected members.
 /// @brief A class which derives from option and exposes protected members.
 class NakedOption : public Option {
 class NakedOption : public Option {
 public:
 public:
@@ -651,55 +602,4 @@ TEST_F(OptionTest, setEncapsulatedSpace) {
 
 
 }
 }
 
 
-// This test verifies that it is possible to specify custom implementation of
-// the option parsing algorithm by installing a callback function.
-TEST_F(OptionTest, unpackCallback) {
-    // Create a buffer which holds two sub options.
-    const char opt_data[] = {
-        0x00, 0x01,  // sub option code  = 1
-        0x00, 0x02,  // sub option length = 2
-        0x00, 0x01,  // sub option data (2 bytes)
-        0x00, 0x02,  // sub option code = 2
-        0x00, 0x02,  // sub option length = 2
-        0x01, 0x01   // sub option data (2 bytes)
-    };
-    OptionBuffer opt_buf(opt_data, opt_data + sizeof(opt_data));
-
-    // Make sure that the flag which indicates if the callback function has
-    // been called is not set. Otherwise, our test doesn't make sense.
-    CustomUnpackCallback cb;
-    ASSERT_FALSE(cb.executed_);
-    // Create an option and install a callback.
-    NakedOption option;
-    // Parameters from _1 to _5 are placeholders for the actual values
-    // to be passed to the callback function. See: boost::bind documentation
-    // at http://www.boost.org/doc/libs/1_54_0/libs/bind/bind.html.
-    // Also, see UnpackOptionsCallback in option.h for description of the
-    // parameter values.
-    option.setCallback(boost::bind(&CustomUnpackCallback::execute, &cb,
-                                   _1, _2, _3, _4, _5));
-    // Parse options. It should result in a call to our callback function.
-    // This function uses LibDHCP to parse options so they should be parsed
-    // correctly.
-    ASSERT_NO_THROW(option.unpackOptions(opt_buf));
-    EXPECT_TRUE(option.getOption(1));
-    EXPECT_TRUE(option.getOption(2));
-    EXPECT_FALSE(option.getOption(3));
-    // The callback should have been registered.
-    EXPECT_TRUE(cb.executed_);
-    // Reset the flag because now we are going to uninstall the callback and
-    // verify that it was NOT called.
-    cb.executed_ = false;
-    // Uninstall the callback.
-    option.setCallback(NULL);
-    ASSERT_NO_THROW(option.unpackOptions(opt_buf));
-    // Options should still get unpacked...
-    EXPECT_TRUE(option.getOption(1));
-    EXPECT_TRUE(option.getOption(2));
-    EXPECT_FALSE(option.getOption(3));
-    // ... but not via callback.
-    EXPECT_FALSE(cb.executed_);
-}
-
-
 }
 }

+ 0 - 83
src/lib/dhcp/tests/pkt4_unittest.cc

@@ -39,48 +39,6 @@ using boost::scoped_ptr;
 
 
 namespace {
 namespace {
 
 
-/// @brief A class which contains a custom callback function to unpack options.
-///
-/// This is a class used by the tests which verify that the custom callback
-/// functions can be installed to unpack options from a message. When the
-/// callback function is called, the executed_ member is set to true to allow
-/// verification that the callback was really called. Internally, this class
-/// uses libdhcp++ to unpack options so the options parsing algorithm remains
-/// unchanged after installation of the callback.
-class CustomUnpackCallback {
-public:
-
-    /// @brief Constructor
-    ///
-    /// Marks that callback hasn't been called.
-    CustomUnpackCallback()
-        : executed_(false) {
-    }
-
-    /// @brief A callback
-    ///
-    /// Contains custom implementation of the callback.
-    ///
-    /// @param buf a A buffer holding options in on-wire format.
-    /// @param option_space A name of the option space being encapsulated by
-    /// the option being parsed.
-    /// @param [out] options A reference to the collection where parsed options
-    /// will be stored.
-    /// @return An offset to the first byte after last parsed option.
-    size_t execute(const OptionBuffer& buf,
-                   const std::string& option_space,
-                   isc::dhcp::OptionCollection& options) {
-        // Set the executed_ member to true to allow verification that the
-        // callback has been actually called.
-        executed_ = true;
-        // Use default implementation of the unpack algorithm to parse options.
-        return (LibDHCP::unpackOptions4(buf, option_space, options));
-    }
-
-    /// A flag which indicates if callback function has been called.
-    bool executed_;
-};
-
 /// V4 Options being used for pack/unpack testing.
 /// V4 Options being used for pack/unpack testing.
 /// For test simplicity, all selected options have
 /// For test simplicity, all selected options have
 /// variable length data so as there are no restrictions
 /// variable length data so as there are no restrictions
@@ -766,47 +724,6 @@ TEST_F(Pkt4Test, unpackVendorMalformed) {
     EXPECT_THROW(too_short_pkt->unpack(), InvalidOptionValue);
     EXPECT_THROW(too_short_pkt->unpack(), InvalidOptionValue);
 }
 }
 
 
-// This test verifies that it is possible to specify custom implementation of
-// the option parsing algorithm by installing a callback function.
-TEST_F(Pkt4Test, unpackOptionsWithCallback) {
-    vector<uint8_t> expectedFormat = generateTestPacket2();
-
-    expectedFormat.push_back(0x63);
-    expectedFormat.push_back(0x82);
-    expectedFormat.push_back(0x53);
-    expectedFormat.push_back(0x63);
-
-    for (size_t i = 0; i < sizeof(v4_opts); i++) {
-        expectedFormat.push_back(v4_opts[i]);
-    }
-
-    // now expectedFormat contains fixed format and 5 options
-
-    boost::shared_ptr<Pkt4> pkt(new Pkt4(&expectedFormat[0],
-                                expectedFormat.size()));
-
-    CustomUnpackCallback cb;
-    pkt->setCallback(boost::bind(&CustomUnpackCallback::execute, &cb,
-                                 _1, _2, _3));
-
-    ASSERT_FALSE(cb.executed_);
-
-    EXPECT_NO_THROW(pkt->unpack());
-
-    EXPECT_TRUE(cb.executed_);
-    verifyParsedOptions(pkt);
-
-    // Reset the indicator to perform another check: uninstall the callback.
-    cb.executed_ = false;
-    // By setting the callback to NULL we effectively uninstall the callback.
-    pkt->setCallback(NULL);
-    // Do another unpack.
-    EXPECT_NO_THROW(pkt->unpack());
-    // Callback should not be executed.
-    EXPECT_FALSE(cb.executed_);
-
-}
-
 // This test verifies methods that are used for manipulating meta fields
 // This test verifies methods that are used for manipulating meta fields
 // i.e. fields that are not part of DHCPv4 (e.g. interface name).
 // i.e. fields that are not part of DHCPv4 (e.g. interface name).
 TEST_F(Pkt4Test, metaFields) {
 TEST_F(Pkt4Test, metaFields) {

+ 0 - 92
src/lib/dhcp/tests/pkt6_unittest.cc

@@ -41,55 +41,6 @@ using boost::scoped_ptr;
 
 
 namespace {
 namespace {
 
 
-/// @brief A class which contains a custom callback function to unpack options.
-///
-/// This is a class used by the tests which verify that the custom callback
-/// functions can be installed to unpack options from a message. When the
-/// callback function is called, the executed_ member is set to true to allow
-/// verification that the callback was really called. Internally, this class
-/// uses libdhcp++ to unpack options so the options parsing algorithm remains
-/// unchanged after installation of the callback.
-class CustomUnpackCallback {
-public:
-
-    /// @brief Constructor
-    ///
-    /// Marks that callback hasn't been called.
-    CustomUnpackCallback()
-        : executed_(false) {
-    }
-
-    /// @brief A callback
-    ///
-    /// Contains custom implementation of the callback.
-    ///
-    /// @param buf a A buffer holding options in on-wire format.
-    /// @param option_space A name of the option space encapsulated by the
-    /// option being parsed.
-    /// @param [out] options A reference to the collection where parsed options
-    /// will be stored.
-    /// @param relay_msg_offset Reference to a size_t structure. If specified,
-    /// offset to beginning of relay_msg option will be stored in it.
-    /// @param relay_msg_len reference to a size_t structure. If specified,
-    /// length of the relay_msg option will be stored in it.
-    /// @return An offset to the first byte after last parsed option.
-    size_t execute(const OptionBuffer& buf,
-                   const std::string& option_space,
-                   isc::dhcp::OptionCollection& options,
-                   size_t* relay_msg_offset,
-                   size_t* relay_msg_len) {
-        // Set the executed_ member to true to allow verification that the
-        // callback has been actually called.
-        executed_ = true;
-        // Use default implementation of the unpack algorithm to parse options.
-        return (LibDHCP::unpackOptions6(buf, option_space, options,
-                                        relay_msg_offset, relay_msg_len));
-    }
-
-    /// A flag which indicates if callback function has been called.
-    bool executed_;
-};
-
 class Pkt6Test : public ::testing::Test {
 class Pkt6Test : public ::testing::Test {
 public:
 public:
     Pkt6Test() {
     Pkt6Test() {
@@ -436,49 +387,6 @@ TEST_F(Pkt6Test, unpackVendorMalformed) {
     EXPECT_THROW(too_short_option_pkt->unpack(), OutOfRange);
     EXPECT_THROW(too_short_option_pkt->unpack(), OutOfRange);
 }
 }
 
 
-// This test verifies that it is possible to specify custom implementation of
-// the option parsing algorithm by installing a callback function.
-TEST_F(Pkt6Test, packUnpackWithCallback) {
-    // Create an on-wire representation of the test packet and clone it.
-    Pkt6Ptr pkt(new Pkt6(DHCPV6_SOLICIT, 0x020304));
-    Pkt6Ptr clone = packAndClone(pkt);
-
-    // Install the custom callback function. We expect that this function
-    // will be called to parse options in the packet instead of
-    // LibDHCP::unpackOptions6.
-    CustomUnpackCallback cb;
-    clone->setCallback(boost::bind(&CustomUnpackCallback::execute, &cb,
-                                   _1, _2, _3, _4, _5));
-    // Make sure that the flag which indicates if the callback function has
-    // been called is not set. Otherwise, our test doesn't make sense.
-    ASSERT_FALSE(cb.executed_);
-
-    // Now recreate options list
-    ASSERT_NO_THROW(clone->unpack());
-
-    // An object which holds a callback should now have a flag set which
-    // indicates that callback has been called.
-    EXPECT_TRUE(cb.executed_);
-
-    // transid, message-type should be the same as before
-    EXPECT_EQ(0x020304, clone->getTransid());
-    EXPECT_EQ(DHCPV6_SOLICIT, clone->getType());
-
-    EXPECT_TRUE(clone->getOption(1));
-    EXPECT_TRUE(clone->getOption(2));
-    EXPECT_TRUE(clone->getOption(100));
-    EXPECT_FALSE(clone->getOption(4));
-
-    // Reset the indicator to perform another check: uninstall the callback.
-    cb.executed_ = false;
-    // By setting the callback to NULL we effectively uninstall the callback.
-    clone->setCallback(NULL);
-    // Do another unpack.
-    ASSERT_NO_THROW(clone->unpack());
-    // Callback should not be executed.
-    EXPECT_FALSE(cb.executed_);
-}
-
 // This test verifies that options can be added (addOption()), retrieved
 // This test verifies that options can be added (addOption()), retrieved
 // (getOption(), getOptions()) and deleted (delOption()).
 // (getOption(), getOptions()) and deleted (delOption()).
 TEST_F(Pkt6Test, addGetDelOptions) {
 TEST_F(Pkt6Test, addGetDelOptions) {