-// 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
#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 {
/// 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
///
/// @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 {
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,
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);
}
}
+// 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) {