]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#2517] Added getList for option descriptors
authorFrancis Dupont <fdupont@isc.org>
Sun, 31 Jul 2022 14:23:48 +0000 (16:23 +0200)
committerFrancis Dupont <fdupont@isc.org>
Wed, 3 Aug 2022 13:26:09 +0000 (15:26 +0200)
src/lib/dhcpsrv/cfg_option.h
src/lib/dhcpsrv/tests/cfg_option_unittest.cc

index fedd7cce38b6327eded6d4ff38dbe2729d65ec39..d2631d6f76406a78c405890d66fd6392dd9760c9 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2014-2021 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2022 Internet Systems Consortium, Inc. ("ISC")
 //
 // 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
@@ -22,8 +22,9 @@
 #include <boost/multi_index/member.hpp>
 #include <boost/shared_ptr.hpp>
 #include <stdint.h>
-#include <string>
 #include <list>
+#include <string>
+#include <vector>
 
 namespace isc {
 namespace dhcp {
@@ -33,6 +34,9 @@ class OptionDescriptor;
 /// A pointer to option descriptor.
 typedef boost::shared_ptr<OptionDescriptor> OptionDescriptorPtr;
 
+/// A list of option descriptors.
+typedef std::vector<OptionDescriptor> OptionDescriptorList;
+
 /// @brief Option descriptor.
 ///
 /// Option descriptor holds instance of an option and additional information
@@ -532,14 +536,15 @@ public:
     ///
     /// @note If there are multiple options with the same key, only one will
     /// be returned.  No indication will be given of the presence of others,
-    /// and the instance returned is not determinable.
+    /// and the instance returned is not determinable. So please use
+    /// the next method when multiple instances of the option are expected.
     ///
     /// @param key Option space name or vendor identifier.
     /// @param option_code Code of the option to be returned.
     /// @tparam Selector one of: @c std::string or @c uint32_t
     ///
     /// @return Descriptor of the option. If option hasn't been found, the
-    /// descriptor holds NULL option.
+    /// descriptor holds null option.
     template<typename Selector>
     OptionDescriptor get(const Selector& key,
                          const uint16_t option_code) const {
@@ -560,6 +565,40 @@ public:
         return (*od_itr);
     }
 
+    /// @brief Returns options for the specified key and option code.
+    ///
+    /// The key should be a string, in which case it specifies an option space
+    /// name, or an uint32_t value, in which case it specifies a vendor
+    /// identifier.
+    ///
+    /// @param key Option space name or vendor identifier.
+    /// @param option_code Code of the option to be returned.
+    /// @tparam Selector one of: @c std::string or @c uint32_t
+    ///
+    /// @return List of Descriptors of the option.
+    template<typename Selector>
+    OptionDescriptorList getList(const Selector& key,
+                                 const uint16_t option_code) const {
+
+        OptionDescriptorList list;
+        // Check for presence of options.
+        OptionContainerPtr options = getAll(key);
+        if (!options || options->empty()) {
+            return (list);
+        }
+
+        // Some options present, locate the one we are interested in.
+        const OptionContainerTypeIndex& idx = options->get<1>();
+        OptionContainerTypeRange range = idx.equal_range(option_code);
+        // This code copies descriptors and can be optimized not doing this.
+        for (OptionContainerTypeIndex::const_iterator od_itr = range.first;
+             od_itr != range.second; ++od_itr) {
+            list.push_back(*od_itr);
+        }
+
+        return (list);
+    }
+
     /// @brief Deletes option for the specified option space and option code.
     ///
     /// If the option is encapsulated within some non top level option space,
index 2e1acb2faac89ef5ed821767dcdfc658c4d71e55..2f1408a4a5a271a4c55ce31d8e294a00e36e76aa 100644 (file)
@@ -921,7 +921,7 @@ TEST_F(CfgOptionTest, get) {
         std::ostringstream stream;
         // First, try the invalid option space name.
         OptionDescriptor desc = cfg.get("isc", code);
-        // Returned descriptor should contain NULL option ptr.
+        // Returned descriptor should contain null option ptr.
         EXPECT_FALSE(desc.option_);
         // Now, try the valid option space.
         desc = cfg.get(DHCP6_OPTION_SPACE, code);
@@ -931,6 +931,95 @@ TEST_F(CfgOptionTest, get) {
     }
 }
 
+// This test verifies that multiple options can be retrieved from the
+// configuration using option code and option space.
+TEST_F(CfgOptionTest, getList) {
+    CfgOption cfg;
+
+    // Add twice 10 options to a "dhcp4" option space in the subnet.
+    for (uint16_t code = 100; code < 110; ++code) {
+        OptionPtr option(new Option(Option::V4, code, OptionBuffer(10, 0xFF)));
+        ASSERT_NO_THROW(cfg.add(option, false, DHCP4_OPTION_SPACE));
+        OptionPtr option2(new Option(Option::V4, code, OptionBuffer(10, 0xEE)));
+        ASSERT_NO_THROW(cfg.add(option2, false, DHCP4_OPTION_SPACE));
+    }
+
+    // Check that we can get each added option descriptors.
+    for (uint16_t code = 100; code < 110; ++code) {
+        std::ostringstream stream;
+        // First, try the invalid option space name.
+        OptionDescriptorList list = cfg.getList("isc", code);
+        // Returned descriptor list should be empty.
+        EXPECT_TRUE(list.empty());
+        // Now, try the valid option space.
+        list = cfg.getList(DHCP4_OPTION_SPACE, code);
+        // Test that the option code matches the expected code.
+        ASSERT_EQ(2, list.size());
+        OptionDescriptor desc = list[0];
+        ASSERT_TRUE(desc.option_);
+        EXPECT_EQ(code, desc.option_->getType());
+        OptionBuffer content = desc.option_->getData();
+        ASSERT_EQ(10, content.size());
+        uint8_t val = content[8];
+        EXPECT_TRUE((val == 0xFF) || (val == 0xEE));
+        desc =  list[1];
+        ASSERT_TRUE(desc.option_);
+        EXPECT_EQ(code, desc.option_->getType());
+        content = desc.option_->getData();
+        ASSERT_EQ(10, content.size());
+        if (val == 0xFF) {
+            EXPECT_EQ(0xEE, content[4]);
+        } else {
+            EXPECT_EQ(0xFF, content[4]);
+        }
+    }
+}
+
+// This test verifies that multiple options can be retrieved from the
+// configuration using option code and vendor space.
+TEST_F(CfgOptionTest, getListVendor) {
+    CfgOption cfg;
+    std::string vendor_space("vendor-12345678");
+
+    // Add twice 10 options to a "dhcp4" option space in the subnet.
+    for (uint16_t code = 100; code < 110; ++code) {
+        OptionPtr option(new Option(Option::V4, code, OptionBuffer(10, 0xFF)));
+        ASSERT_NO_THROW(cfg.add(option, false, vendor_space));
+        OptionPtr option2(new Option(Option::V4, code, OptionBuffer(10, 0xEE)));
+        ASSERT_NO_THROW(cfg.add(option2, false, vendor_space));
+    }
+
+    // Check that we can get each added option descriptors.
+    for (uint16_t code = 100; code < 110; ++code) {
+        std::ostringstream stream;
+        // First, try the invalid option space name.
+        OptionDescriptorList list = cfg.getList(11111111, code);
+        // Returned descriptor list should be empty.
+        EXPECT_TRUE(list.empty());
+        // Now, try the valid option space.
+        list = cfg.getList(12345678, code);
+        // Test that the option code matches the expected code.
+        ASSERT_EQ(2, list.size());
+        OptionDescriptor desc = list[0];
+        ASSERT_TRUE(desc.option_);
+        EXPECT_EQ(code, desc.option_->getType());
+        OptionBuffer content = desc.option_->getData();
+        ASSERT_EQ(10, content.size());
+        uint8_t val = content[8];
+        EXPECT_TRUE((val == 0xFF) || (val == 0xEE));
+        desc =  list[1];
+        ASSERT_TRUE(desc.option_);
+        EXPECT_EQ(code, desc.option_->getType());
+        content = desc.option_->getData();
+        ASSERT_EQ(10, content.size());
+        if (val == 0xFF) {
+            EXPECT_EQ(0xEE, content[4]);
+        } else {
+            EXPECT_EQ(0xFF, content[4]);
+        }
+    }
+}
+
 // This test verifies that the same options can be added to the configuration
 // under different option space.
 TEST_F(CfgOptionTest, addNonUniqueOptions) {