BIND 10 trac3151, updated. f67708b12cc011dcaa6a6352792b42f154527bec [3151] Added prefix delegation configuration support to b10-dhcp6

BIND 10 source code commits bind10-changes at lists.isc.org
Mon Sep 23 20:20:21 UTC 2013


The branch, trac3151 has been updated
       via  f67708b12cc011dcaa6a6352792b42f154527bec (commit)
      from  4e7c1fb3533a53bd6cd21b563553f1e40f9bdf30 (commit)

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

- Log -----------------------------------------------------------------
commit f67708b12cc011dcaa6a6352792b42f154527bec
Author: Thomas Markwalder <tmark at isc.org>
Date:   Mon Sep 23 16:17:44 2013 -0400

    [3151] Added prefix delegation configuration support to b10-dhcp6
    
    Added entries to dhcp6.spec file to allow a list of prefix
    delegation pools to be defined for subnet6 entries, along
    with the necessary parsing logic in dhcp6.

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

Summary of changes:
 configure.ac                                       |    1 +
 src/bin/dhcp6/config_parser.cc                     |  167 ++++++++++++++
 src/bin/dhcp6/dhcp6.spec                           |   36 +++-
 src/bin/dhcp6/tests/config_parser_unittest.cc      |  227 ++++++++++++++++++++
 .../tests/test_data_files_config.h.in              |    4 +-
 src/lib/dhcpsrv/tests/test_libraries.h             |    6 +-
 6 files changed, 434 insertions(+), 7 deletions(-)
 copy src/bin/{d2 => dhcp6}/tests/test_data_files_config.h.in (86%)

-----------------------------------------------------------------------
diff --git a/configure.ac b/configure.ac
index 2cc91660..66381dd 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1463,6 +1463,7 @@ AC_OUTPUT([doc/version.ent
            src/bin/auth/gen-statisticsitems.py.pre
            src/bin/dhcp4/spec_config.h.pre
            src/bin/dhcp6/spec_config.h.pre
+           src/bin/dhcp6/tests/test_data_files_config.h
            src/bin/d2/spec_config.h.pre
            src/bin/d2/tests/test_data_files_config.h
            src/bin/tests/process_rename_test.py
diff --git a/src/bin/dhcp6/config_parser.cc b/src/bin/dhcp6/config_parser.cc
index 57af60e..a31f73f 100644
--- a/src/bin/dhcp6/config_parser.cc
+++ b/src/bin/dhcp6/config_parser.cc
@@ -166,6 +166,171 @@ protected:
     }
 };
 
+class PdPoolParser : public DhcpConfigParser {
+public:
+
+    /// @brief Constructor.
+    ///
+    /// @param param_name name of the parameter. Note, it is passed through
+    /// but unused, parameter is currently always "Dhcp6/subnet6[X]/pool"
+    /// @param pools storage container in which to store the parsed pool
+    /// upon "commit"
+    PdPoolParser(const std::string&,  PoolStoragePtr pools)
+        : uint32_values_(new Uint32Storage()),
+          string_values_(new StringStorage()), pools_(pools) {
+        if (!pools_) {
+            isc_throw(isc::dhcp::DhcpConfigError,
+                      "PdPoolParser context storage may not be NULL");
+        }
+    }
+
+    /// @brief Builds a prefix delegation pool from the given configuration
+    ///
+    /// This function parses configuration entries and creates an instance
+    /// of a dhcp::Pool6 configured for prefix delegation.
+    ///
+    /// @param pd_pool_ pointer to an element that holds configuration entries
+    /// that define a prefix delegation pool.
+    ///
+    /// @throw DhcpConfigError if configuration parsing fails.
+    virtual void build(ConstElementPtr pd_pool_) {
+        // Parse the elements that make up the option definition.
+        BOOST_FOREACH(ConfigPair param, pd_pool_->mapValue()) {
+            std::string entry(param.first);
+            ParserPtr parser;
+            if (entry == "prefix") {
+                StringParserPtr str_parser(new StringParser(entry,
+                                                            string_values_));
+                parser = str_parser;
+            } else if (entry == "prefix-len" || entry == "delegated-len") {
+                Uint32ParserPtr code_parser(new Uint32Parser(entry,
+                                                             uint32_values_));
+                parser = code_parser;
+            } else {
+                isc_throw(DhcpConfigError, "invalid parameter: " << entry);
+            }
+
+            parser->build(param.second);
+            parser->commit();
+        }
+
+        try {
+            // We should now have all of the pool elements we need to create
+            // the pool.  Fetch them and pass them into the Pool6 constructor.
+            // The constructor is expected to enforce any value validation.
+            const std::string addr_str = string_values_->getParam("prefix");
+            IOAddress addr(addr_str);
+
+            uint32_t prefix_len = uint32_values_->getParam("prefix-len");
+
+            uint32_t delegated_len = uint32_values_->getParam("delegated-len");
+
+            // Attempt to construct the local pool.
+            pool_.reset(new Pool6(Lease::TYPE_PD, addr, prefix_len,
+                                 delegated_len));
+        } catch (const std::exception& ex) {
+            isc_throw(isc::dhcp::DhcpConfigError,
+                      "PdPoolParser failed to build pool: " << ex.what());
+        }
+    }
+
+    // @brief Commits the constructed local pool to the pool storage.
+    virtual void commit() {
+        // Add the local pool to the external storage ptr.
+        pools_->push_back(pool_);
+    }
+
+protected:
+    /// Storage for subnet-specific integer values.
+    Uint32StoragePtr uint32_values_;
+
+    /// Storage for subnet-specific string values.
+    StringStoragePtr string_values_;
+
+    /// Parsers are stored here.
+    ParserCollection parsers_;
+
+    /// Pointer to the created pool object.
+    isc::dhcp::Pool6Ptr pool_;
+
+    /// Pointer to storage to which the local pool is written upon commit.
+    isc::dhcp::PoolStoragePtr pools_;
+};
+
+/// @brief Parser for a list of prefix delegation pools.
+///
+/// This parser iterates over a list of prefix delegation pool entries and
+/// creates pool instances for each one. If the parsing is successful, the
+/// collection of pools is committed to the provided storage.
+class PdPoolListParser : public DhcpConfigParser {
+public:
+    /// @brief Constructor.
+    ///
+    /// @param dummy first argument is ignored, all Parser constructors
+    /// accept string as first argument.
+    /// @param storage is the pool storage in which to store the parsed
+    /// pools in this list
+    /// @throw isc::dhcp::DhcpConfigError if storage is null.
+    PdPoolListParser(const std::string&, PoolStoragePtr pools)
+        : local_pools_(new PoolStorage()), pools_(pools) {
+        if (!pools_) {
+            isc_throw(isc::dhcp::DhcpConfigError,
+                      "PdPoolListParser pools storage may not be NULL");
+        }
+    }
+
+    /// @brief Parse configuration entries.
+    ///
+    /// This function parses configuration entries and creates instances
+    /// of prefix delegation pools .
+    ///
+    /// @param pd_pool_list pointer to an element that holds entries
+    /// that define a prefix delegation pool.
+    ///
+    /// @throw DhcpConfigError if configuration parsing fails.
+    void build(isc::data::ConstElementPtr pd_pool_list) {
+        // Make sure the local list is empty.
+        local_pools_.reset(new PoolStorage());
+
+        // Make sure we have a configuration elements to parse.
+        if (!pd_pool_list) {
+            isc_throw(DhcpConfigError,
+                      "PdPoolListParser: list of pool definitions is empty");
+        }
+
+        // Loop through the list of pd pools.
+        BOOST_FOREACH(ConstElementPtr pd_pool, pd_pool_list->listValue()) {
+            boost::shared_ptr<PdPoolParser>
+                // Create the PdPool parser.
+                parser(new PdPoolParser("pd-pool", local_pools_));
+                // Build the pool instance
+                parser->build(pd_pool);
+                // Commit the pool to the local list of pools.
+                parser->commit();
+        }
+    }
+
+    /// @brief  Commits the pools created to the external storage area.
+    ///
+    /// Note that this method adds the local list of pools to the storage area
+    /// rather than replacing its contents.  This permits other parsers to
+    /// contribute to the set of pools.
+    void commit() {
+        // local_pools_ holds the values produced by the build function.
+        // At this point parsing should have completed successfully so
+        // we can append new data to the supplied storage.
+        pools_->insert(pools_->end(), local_pools_->begin(),
+                       local_pools_->end());
+    }
+
+private:
+    /// @brief storage for local pools
+    PoolStoragePtr local_pools_;
+
+    /// @brief External storage where pools are stored upon list commit.
+    PoolStoragePtr pools_;
+};
+
 /// @brief This class parses a single IPv6 subnet.
 ///
 /// This is the IPv6 derivation of the SubnetConfigParser class and it parses
@@ -219,6 +384,8 @@ protected:
             parser = new StringParser(config_id, string_values_);
         } else if (config_id.compare("pool") == 0) {
             parser = new Pool6Parser(config_id, pools_);
+        } else if (config_id.compare("pd-pools") == 0) {
+            parser = new PdPoolListParser(config_id, pools_);
         } else if (config_id.compare("option-data") == 0) {
            parser = new OptionDataListParser(config_id, options_,
                                              global_context_,
diff --git a/src/bin/dhcp6/dhcp6.spec b/src/bin/dhcp6/dhcp6.spec
index 634b046..3810fad 100644
--- a/src/bin/dhcp6/dhcp6.spec
+++ b/src/bin/dhcp6/dhcp6.spec
@@ -16,7 +16,7 @@
           "item_default": ""
         }
       },
- 
+
       { "item_name": "interfaces",
         "item_type": "list",
         "item_optional": false,
@@ -254,6 +254,38 @@
                         "item_default": ""
                     }
                 },
+                {
+                  "item_name": "pd-pools",
+                  "item_type": "list",
+                  "item_optional": true,
+                  "item_default": [],
+                  "list_item_spec":
+                  {
+                      "item_name": "pd-pool",
+                      "item_type": "map",
+                      "item_optional": false,
+                      "item_default": {},
+                      "map_item_spec": [
+                      {
+                          "item_name": "prefix",
+                          "item_type": "string",
+                          "item_optional": false,
+                          "item_default": ""
+                       },
+                       {
+                           "item_name": "prefix-len",
+                           "item_type": "integer",
+                           "item_optional": false,
+                           "item_default": 128
+                       },
+                       {
+                           "item_name": "delegated-len",
+                           "item_type": "integer",
+                           "item_optional": false,
+                           "item_default": 128
+                       }]
+                    }
+                },
                 { "item_name": "option-data",
                   "item_type": "list",
                   "item_optional": false,
@@ -313,7 +345,7 @@
 
         {
             "command_name": "libreload",
-            "command_description": "Reloads the current hooks libraries.", 
+            "command_description": "Reloads the current hooks libraries.",
             "command_args": []
         }
     ]
diff --git a/src/bin/dhcp6/tests/config_parser_unittest.cc b/src/bin/dhcp6/tests/config_parser_unittest.cc
index 0c46d26..beed683 100644
--- a/src/bin/dhcp6/tests/config_parser_unittest.cc
+++ b/src/bin/dhcp6/tests/config_parser_unittest.cc
@@ -26,6 +26,7 @@
 #include <dhcpsrv/subnet.h>
 #include <hooks/hooks_manager.h>
 
+#include "test_data_files_config.h"
 #include "test_libraries.h"
 #include "marker_file.h"
 
@@ -53,6 +54,17 @@ using namespace std;
 
 namespace {
 
+std::string specfile(const std::string& name) {
+    return (std::string(DHCP6_SRC_DIR) + "/" + name);
+}
+
+/// @brief Tests that the spec file is valid.
+/// Verifies that the DHCP6 configuration specification file is valid.
+TEST(Dhcp6SpecTest, basicSpec) {
+    ASSERT_NO_THROW(isc::config::
+                    moduleSpecFromFile(specfile("dhcp6.spec")));
+}
+
 class Dhcp6ParserTest : public ::testing::Test {
 public:
     Dhcp6ParserTest() :rcode_(-1), srv_(0) {
@@ -712,6 +724,221 @@ TEST_F(Dhcp6ParserTest, poolPrefixLen) {
     EXPECT_EQ(4000, subnet->getValid());
 }
 
+// Goal of this test is to verify the basic parsing of a prefix delegation
+// pool. It uses a single, valid pd pool.
+TEST_F(Dhcp6ParserTest, pdPoolBasics) {
+
+    ConstElementPtr x;
+
+    // Define a single valid pd pool.
+    string config =
+        "{ \"interfaces\": [ \"*\" ],"
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet6\": [ { "
+        "    \"subnet\": \"2001:db8:1::/64\","
+        "    \"pd-pools\": ["
+        "        { \"prefix\": \"2001:db8:1::\", "
+        "          \"prefix-len\": 64, "
+        "          \"delegated-len\": 128"
+        "        } ],"
+        "\"valid-lifetime\": 4000 }"
+        "] }";
+
+    // Convert the JSON string into Elements.
+    ElementPtr json;
+    ASSERT_NO_THROW(json = Element::fromJSON(config));
+
+    // Verify that DHCP6 configuration processing succeeds.
+    // Returned value must be non-empty ConstElementPtr to config result.
+    // rcode should be 0 which indicates successful configuration processing.
+    EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
+    ASSERT_TRUE(x);
+    comment_ = parseAnswer(rcode_, x);
+    EXPECT_EQ(0, rcode_);
+
+    // Test that we can retrieve the subnet.
+    Subnet6Ptr subnet = CfgMgr::
+                        instance().getSubnet6(IOAddress("2001:db8:1::5"));
+
+    ASSERT_TRUE(subnet);
+
+    // Fetch the collection of PD pools.  It should have 1 entry.
+    PoolCollection pc;
+    ASSERT_NO_THROW(pc = subnet->getPools(Lease::TYPE_PD));
+    EXPECT_EQ(1, pc.size());
+
+    // Get a pointer to the pd pool instance, and verify its contents.
+    Pool6Ptr p6;
+    ASSERT_NO_THROW(p6 = boost::dynamic_pointer_cast<Pool6>(pc[0]));
+    ASSERT_TRUE(p6);
+    EXPECT_EQ("2001:db8:1::", p6->getFirstAddress().toText());
+    EXPECT_EQ(128, p6->getLength());
+}
+
+// Goal of this test is verify that a list of PD pools can be configured.
+TEST_F(Dhcp6ParserTest, pdPoolList) {
+
+    ConstElementPtr x;
+
+    const char* prefixes[] = {
+        "2001:db8:1:1::",
+        "2001:db8:1:2::",
+        "2001:db8:1:3::"
+    };
+
+    string config =
+        "{ \"interfaces\": [ \"*\" ],"
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet6\": [ { "
+        "    \"subnet\": \"2001:db8:1::/40\","
+        "    \"pd-pools\": ["
+        "        { \"prefix\": \"2001:db8:1:01::\", "
+        "          \"prefix-len\": 72, "
+        "          \"delegated-len\": 80"
+        "        },"
+        "        { \"prefix\": \"2001:db8:1:02::\", "
+        "          \"prefix-len\": 72, "
+        "          \"delegated-len\": 88"
+        "        },"
+        "        { \"prefix\": \"2001:db8:1:03::\", "
+        "          \"prefix-len\": 72, "
+        "          \"delegated-len\": 96"
+        "        }"
+        "],"
+        "\"valid-lifetime\": 4000 }"
+        "] }";
+
+    // Convert the JSON string into Elements.
+    ElementPtr json = Element::fromJSON(config);
+    ASSERT_NO_THROW(json = Element::fromJSON(config));
+
+    // Verify that DHCP6 configuration processing succeeds.
+    // Returned value must be non-empty ConstElementPtr to config result.
+    // rcode should be 0 which indicates successful configuration processing.
+    EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
+    ASSERT_TRUE(x);
+    comment_ = parseAnswer(rcode_, x);
+    EXPECT_EQ(0, rcode_);
+
+    // Test that we can retrieve the subnet.
+    Subnet6Ptr subnet = CfgMgr::
+                        instance().getSubnet6(IOAddress("2001:db8:1::5"));
+    ASSERT_TRUE(subnet);
+
+    // Fetch the collection of PD pools.  It should have 3 entries.
+    PoolCollection pc;
+    ASSERT_NO_THROW(pc = subnet->getPools(Lease::TYPE_PD));
+    EXPECT_EQ(3, pc.size());
+
+    // Loop through the pools and verify their contents.
+    for (int i = 0; i < 3; i++) {
+        Pool6Ptr p6;
+        ASSERT_NO_THROW(p6 = boost::dynamic_pointer_cast<Pool6>(pc[i]));
+        ASSERT_TRUE(p6);
+        EXPECT_EQ(prefixes[i], p6->getFirstAddress().toText());
+        EXPECT_EQ((80 + (i * 8)), p6->getLength());
+    }
+}
+
+// Goal of this test is check for proper handling of invalid prefix delegation
+// pool configuration.  It uses an array of invalid configurations to check
+// a variety of configuration errors.
+TEST_F(Dhcp6ParserTest, invalidPdPools) {
+
+    ConstElementPtr x;
+
+    const char *config[] =  {
+        // No prefix.
+        "{ \"interfaces\": [ \"*\" ],"
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet6\": [ { "
+        "    \"subnet\": \"2001:db8:1::/64\","
+        "    \"pd-pools\": ["
+        "        { "
+        "          \"prefix-len\": 64, "
+        "          \"delegated-len\": 128"
+        "        } ],"
+        "\"valid-lifetime\": 4000 }"
+        "] }",
+        // No prefix-len.
+        "{ \"interfaces\": [ \"*\" ],"
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet6\": [ { "
+        "    \"subnet\": \"2001:db8:1::/64\","
+        "    \"pd-pools\": ["
+        "        { \"prefix\": \"2001:db8:1::\", "
+        "          \"delegated-len\": 128"
+        "        } ],"
+        "\"valid-lifetime\": 4000 }"
+        "] }",
+        // No delegated-len.
+        "{ \"interfaces\": [ \"*\" ],"
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet6\": [ { "
+        "    \"subnet\": \"2001:db8:1::/64\","
+        "    \"pd-pools\": ["
+        "        { \"prefix\": \"2001:db8:1::\", "
+        "          \"prefix-len\": 64 "
+        "        } ],"
+        "\"valid-lifetime\": 4000 }"
+        "] }",
+        // Delegated length is too short.
+        "{ \"interfaces\": [ \"*\" ],"
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet6\": [ { "
+        "    \"subnet\": \"2001:db8:1::/64\","
+        "    \"pd-pools\": ["
+        "        { \"prefix\": \"2001:db8:1::\", "
+        "          \"prefix-len\": 128, "
+        "          \"delegated-len\": 64"
+        "        } ],"
+        "\"valid-lifetime\": 4000 }"
+        "] }",
+        // Pool is not within the subnet.
+        "{ \"interfaces\": [ \"*\" ],"
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet6\": [ { "
+        "    \"subnet\": \"2001:db8:1::/64\","
+        "    \"pd-pools\": ["
+        "        { \"prefix\": \"2001:db8:77::\", "
+        "          \"prefix-len\": 64, "
+        "          \"delegated-len\": 128"
+        "        } ],"
+        "\"valid-lifetime\": 4000 }"
+        "] }"
+        };
+
+    ElementPtr json;
+    int num_msgs = sizeof(config)/sizeof(char*);
+    for (int i = 0; i < num_msgs; i++) {
+        // Convert JSON string to Elements.
+        ASSERT_NO_THROW(json = Element::fromJSON(config[i]));
+
+        // Configuration processing should fail without a throw.
+        ASSERT_NO_THROW(x = configureDhcp6Server(srv_, json));
+
+        // Returned value must be non-empty ConstElementPtr to config result.
+        // rcode should be 1 which indicates configuration error.
+        ASSERT_TRUE(x);
+        comment_ = parseAnswer(rcode_, x);
+        EXPECT_EQ(1, rcode_);
+    }
+}
+
 // The goal of this test is to check whether an option definition
 // that defines an option carrying an IPv6 address can be created.
 TEST_F(Dhcp6ParserTest, optionDefIpv6Address) {
diff --git a/src/bin/dhcp6/tests/test_data_files_config.h.in b/src/bin/dhcp6/tests/test_data_files_config.h.in
new file mode 100644
index 0000000..edcc2ac
--- /dev/null
+++ b/src/bin/dhcp6/tests/test_data_files_config.h.in
@@ -0,0 +1,17 @@
+// Copyright (C) 2009  Internet Systems Consortium, Inc. ("ISC")   
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+/// @brief Path to DHCP6 source dir so tests against the dhcp6.spec file
+/// can find it reliably.
+#define DHCP6_SRC_DIR "@abs_top_srcdir@/src/bin/dhcp6"
diff --git a/src/lib/dhcpsrv/tests/test_libraries.h b/src/lib/dhcpsrv/tests/test_libraries.h
index 03fd5af..00ce3f8 100644
--- a/src/lib/dhcpsrv/tests/test_libraries.h
+++ b/src/lib/dhcpsrv/tests/test_libraries.h
@@ -37,13 +37,13 @@ namespace {
 
 // Library with load/unload functions creating marker files to check their
 // operation.
-static const char* CALLOUT_LIBRARY_1 = "/home/thomson/devel/bind10/src/lib/dhcpsrv/tests/.libs/libco1"
+static const char* CALLOUT_LIBRARY_1 = "/Users/tmark/build/trac3151/bind10/src/lib/dhcpsrv/tests/.libs/libco1"
                                            DLL_SUFFIX;
-static const char* CALLOUT_LIBRARY_2 = "/home/thomson/devel/bind10/src/lib/dhcpsrv/tests/.libs/libco2"
+static const char* CALLOUT_LIBRARY_2 = "/Users/tmark/build/trac3151/bind10/src/lib/dhcpsrv/tests/.libs/libco2"
                                            DLL_SUFFIX;
 
 // Name of a library which is not present.
-static const char* NOT_PRESENT_LIBRARY = "/home/thomson/devel/bind10/src/lib/dhcpsrv/tests/.libs/libnothere"
+static const char* NOT_PRESENT_LIBRARY = "/Users/tmark/build/trac3151/bind10/src/lib/dhcpsrv/tests/.libs/libnothere"
                                          DLL_SUFFIX;
 } // anonymous namespace
 



More information about the bind10-changes mailing list