summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Markwalder <tmark@isc.org>2024-10-30 19:31:18 +0100
committerThomas Markwalder <tmark@isc.org>2024-11-26 18:19:56 +0100
commita41077e5dc26924e95cb6e581cd1e4c59c4444bf (patch)
tree4b41b30b1270285e08a42eedd60e9a289c984a50
parent[#3661] addressed review comments (diff)
downloadkea-a41077e5dc26924e95cb6e581cd1e4c59c4444bf.tar.xz
kea-a41077e5dc26924e95cb6e581cd1e4c59c4444bf.zip
[#3592] modified in lib dhcp and dhcpsrv
src/lib/dhcp/classify.* ClientClasses:intersects() - new function src/lib/dhcp/tests/classify_unittest.cc TEST(ClassifyTest, ClientClassesIntersects) - new test src/lib/dhcpsrv/cfg_option.cc OptionDescriptor::allowedForClientClasses() use inet intersects() function src/lib/dhcpsrv/dhcpsrv_messages.mes DHCPSRV_CLIENT_CLASS_DEPRECATED - new message src/lib/dhcpsrv/network.* Network - replaced client_class_ string with client_classes_ container Network::clientSupported() - uses new intersects() function Network::allowClientClass() - modified to insert Network::toElement() - updated src/lib/dhcpsrv/parsers/base_network_parser.* BaseNetworkParser::getClientClassesElem() - new function src/lib/dhcpsrv/parsers/dhcp_parsers.cc src/lib/dhcpsrv/parsers/shared_network_parser.cc Updated parsers to use BaseNetworkParser::getClientClassesElem() src/lib/dhcpsrv/parsers/simple_parser4.cc src/lib/dhcpsrv/parsers/simple_parser6.cc Added client-classes src/lib/dhcpsrv/pool.* replaced client_class_ string with client_classes_ container Pool::clientSupported()- use new intersects() function src/lib/dhcpsrv/shared_network.cc ShareNetwork::getPreferredSubnet() - updated src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc Updated tests src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc TEST_F(DhcpParserTest, deprecatedClientClassSubnet4) TEST_F(DhcpParserTest, deprecatedClientClassSubnet6) { TEST_F(DhcpParserTest, deprecatedClientClassPool4) { TEST_F(DhcpParserTest, deprecatedClientClassPool6) { src/lib/dhcpsrv/tests/network_unittest.cc Removed inheritance support check src/lib/dhcpsrv/tests/pool_unittest.cc Updated tests src/lib/dhcpsrv/tests/shared_network_parser_unittest.cc TEST_F(SharedNetwork4ParserTest, deprecatedClientClass) TEST_F(SharedNetwork6ParserTest, deprecatedClientClass) src/lib/dhcpsrv/tests/shared_network_unittest.cc src/lib/dhcpsrv/tests/subnet_unittest.cc Updated tests src/lib/dhcpsrv/testutils/generic_cb_dhcp4_unittest.cc src/lib/dhcpsrv/testutils/generic_cb_dhcp6_unittest.cc Updated tests
-rw-r--r--src/lib/dhcp/classify.cc20
-rw-r--r--src/lib/dhcp/classify.h13
-rw-r--r--src/lib/dhcp/tests/classify_unittest.cc25
-rw-r--r--src/lib/dhcpsrv/cfg_option.cc17
-rw-r--r--src/lib/dhcpsrv/dhcpsrv_messages.cc2
-rw-r--r--src/lib/dhcpsrv/dhcpsrv_messages.h1
-rw-r--r--src/lib/dhcpsrv/dhcpsrv_messages.mes7
-rw-r--r--src/lib/dhcpsrv/network.cc32
-rw-r--r--src/lib/dhcpsrv/network.h49
-rw-r--r--src/lib/dhcpsrv/parsers/base_network_parser.cc37
-rw-r--r--src/lib/dhcpsrv/parsers/base_network_parser.h10
-rw-r--r--src/lib/dhcpsrv/parsers/dhcp_parsers.cc18
-rw-r--r--src/lib/dhcpsrv/parsers/shared_network_parser.cc24
-rw-r--r--src/lib/dhcpsrv/parsers/simple_parser4.cc5
-rw-r--r--src/lib/dhcpsrv/parsers/simple_parser6.cc9
-rw-r--r--src/lib/dhcpsrv/pool.cc41
-rw-r--r--src/lib/dhcpsrv/pool.h39
-rw-r--r--src/lib/dhcpsrv/shared_network.cc2
-rw-r--r--src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc19
-rw-r--r--src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc25
-rw-r--r--src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc215
-rw-r--r--src/lib/dhcpsrv/tests/network_unittest.cc7
-rw-r--r--src/lib/dhcpsrv/tests/pool_unittest.cc12
-rw-r--r--src/lib/dhcpsrv/tests/shared_network_parser_unittest.cc97
-rw-r--r--src/lib/dhcpsrv/tests/shared_network_unittest.cc6
-rw-r--r--src/lib/dhcpsrv/tests/subnet_unittest.cc14
-rw-r--r--src/lib/dhcpsrv/testutils/generic_cb_dhcp4_unittest.cc6
-rw-r--r--src/lib/dhcpsrv/testutils/generic_cb_dhcp6_unittest.cc6
28 files changed, 565 insertions, 193 deletions
diff --git a/src/lib/dhcp/classify.cc b/src/lib/dhcp/classify.cc
index 828b137604..7a001ba621 100644
--- a/src/lib/dhcp/classify.cc
+++ b/src/lib/dhcp/classify.cc
@@ -57,6 +57,26 @@ ClientClasses::contains(const ClientClass& x) const {
return (idx.count(x) != 0);
}
+bool
+ClientClasses::intersects(const ClientClasses& cclasses) const {
+ if (cclasses.size() > size()) {
+ for (const auto& cclass : *this) {
+ if (cclasses.contains(cclass)) {
+ return (true);
+ }
+ }
+ }
+ else {
+ for (const auto& cclass : cclasses) {
+ if (contains(cclass)) {
+ return (true);
+ }
+ }
+ }
+
+ return (false);
+}
+
std::string
ClientClasses::toText(const std::string& separator) const {
std::stringstream s;
diff --git a/src/lib/dhcp/classify.h b/src/lib/dhcp/classify.h
index d617952690..69cdffd417 100644
--- a/src/lib/dhcp/classify.h
+++ b/src/lib/dhcp/classify.h
@@ -21,10 +21,7 @@
/// @file classify.h
///
/// @brief Defines elements for storing the names of client classes
-///
-/// This file defines common elements used to track the client classes
-/// that may be associated with a given packet. In order to minimize the
-/// exposure of the DHCP library to server side concepts such as client
+/// /// This file defines common elements used to track the client classes /// that may be associated with a given packet. In order to minimize the /// exposure of the DHCP library to server side concepts such as client
/// classification the classes herein provide a mechanism to maintain lists
/// of class names, rather than the classes they represent. It is the
/// upper layers' prerogative to use these names as they see fit.
@@ -200,6 +197,14 @@ namespace dhcp {
}
/// @}
+ /// @brief returns whether this container has at least one class
+ /// in given container.
+ ///
+ /// @param cclasses list of classes to check for intersection with
+ /// @return true if this container has at least one class that is
+ /// also in cclasses, false otherwise.
+ bool intersects(const ClientClasses& cclasses) const;
+
/// @brief returns if class x belongs to the defined classes
///
/// @param x client class to be checked
diff --git a/src/lib/dhcp/tests/classify_unittest.cc b/src/lib/dhcp/tests/classify_unittest.cc
index 075af19547..f635817e63 100644
--- a/src/lib/dhcp/tests/classify_unittest.cc
+++ b/src/lib/dhcp/tests/classify_unittest.cc
@@ -266,3 +266,28 @@ TEST(ClassifyTest, ClientClassesFromElement) {
++cclass;
EXPECT_EQ(*cclass, "bar");
}
+
+// Check that the ClientClasses::intersects function works.
+TEST(ClassifyTest, ClientClassesIntersects) {
+ ClientClasses classes1;
+ ClientClasses classes2;
+
+ EXPECT_FALSE(classes1.intersects(classes2));
+ EXPECT_FALSE(classes2.intersects(classes1));
+
+ classes1.insert("one");
+
+ EXPECT_FALSE(classes1.intersects(classes2));
+ EXPECT_FALSE(classes2.intersects(classes1));
+
+ classes2.insert("two");
+ classes2.insert("three");
+
+ EXPECT_FALSE(classes1.intersects(classes2));
+ EXPECT_FALSE(classes2.intersects(classes1));
+
+ classes2.insert("one");
+
+ EXPECT_TRUE(classes1.intersects(classes2));
+ EXPECT_TRUE(classes2.intersects(classes1));
+}
diff --git a/src/lib/dhcpsrv/cfg_option.cc b/src/lib/dhcpsrv/cfg_option.cc
index 4471fcd167..5fbb2dee5b 100644
--- a/src/lib/dhcpsrv/cfg_option.cc
+++ b/src/lib/dhcpsrv/cfg_option.cc
@@ -65,22 +65,7 @@ OptionDescriptor::allowedForClientClasses(const ClientClasses& cclasses) const {
return (true);
}
- if (cclasses.size() > client_classes_.size()) {
- for (const auto& cclass : client_classes_) {
- if (cclasses.contains(cclass)) {
- return (true);
- }
- }
- }
- else {
- for (const auto& cclass : cclasses) {
- if (client_classes_.contains(cclass)) {
- return (true);
- }
- }
- }
-
- return (false);
+ return (client_classes_.intersects(cclasses));
}
CfgOption::CfgOption()
diff --git a/src/lib/dhcpsrv/dhcpsrv_messages.cc b/src/lib/dhcpsrv/dhcpsrv_messages.cc
index 34e868c03b..51c71a4714 100644
--- a/src/lib/dhcpsrv/dhcpsrv_messages.cc
+++ b/src/lib/dhcpsrv/dhcpsrv_messages.cc
@@ -47,6 +47,7 @@ extern const isc::log::MessageID DHCPSRV_CFGMGR_USE_ADDRESS = "DHCPSRV_CFGMGR_US
extern const isc::log::MessageID DHCPSRV_CFGMGR_USE_ALLOCATOR = "DHCPSRV_CFGMGR_USE_ALLOCATOR";
extern const isc::log::MessageID DHCPSRV_CFGMGR_USE_UNICAST = "DHCPSRV_CFGMGR_USE_UNICAST";
extern const isc::log::MessageID DHCPSRV_CLASS_WITH_ADDTIONAL_AND_LIFETIMES = "DHCPSRV_CLASS_WITH_ADDTIONAL_AND_LIFETIMES";
+extern const isc::log::MessageID DHCPSRV_CLIENT_CLASS_DEPRECATED = "DHCPSRV_CLIENT_CLASS_DEPRECATED";
extern const isc::log::MessageID DHCPSRV_CLOSE_DB = "DHCPSRV_CLOSE_DB";
extern const isc::log::MessageID DHCPSRV_DDNS_TTL_PERCENT_TOO_SMALL = "DHCPSRV_DDNS_TTL_PERCENT_TOO_SMALL";
extern const isc::log::MessageID DHCPSRV_DHCP4O6_RECEIVED_BAD_PACKET = "DHCPSRV_DHCP4O6_RECEIVED_BAD_PACKET";
@@ -217,6 +218,7 @@ const char* values[] = {
"DHCPSRV_CFGMGR_USE_ALLOCATOR", "using the %1 allocator for %2 leases in subnet %3",
"DHCPSRV_CFGMGR_USE_UNICAST", "listening on unicast address %1, on interface %2",
"DHCPSRV_CLASS_WITH_ADDTIONAL_AND_LIFETIMES", "class: %1 has 'only-in-additional-list' true while specifying one or more lease life time values. Life time values will be ignored.",
+ "DHCPSRV_CLIENT_CLASS_DEPRECATED", "The parameter 'client-class' is deprecated. Use 'client-classes' list parameter instead",
"DHCPSRV_CLOSE_DB", "closing currently open %1 database",
"DHCPSRV_DDNS_TTL_PERCENT_TOO_SMALL", "ddns-ttl-percent %1 of lease lifetime %2 is too small, ignoring it",
"DHCPSRV_DHCP4O6_RECEIVED_BAD_PACKET", "received bad DHCPv4o6 packet: %1",
diff --git a/src/lib/dhcpsrv/dhcpsrv_messages.h b/src/lib/dhcpsrv/dhcpsrv_messages.h
index a0ae287b01..b586981a1c 100644
--- a/src/lib/dhcpsrv/dhcpsrv_messages.h
+++ b/src/lib/dhcpsrv/dhcpsrv_messages.h
@@ -48,6 +48,7 @@ extern const isc::log::MessageID DHCPSRV_CFGMGR_USE_ADDRESS;
extern const isc::log::MessageID DHCPSRV_CFGMGR_USE_ALLOCATOR;
extern const isc::log::MessageID DHCPSRV_CFGMGR_USE_UNICAST;
extern const isc::log::MessageID DHCPSRV_CLASS_WITH_ADDTIONAL_AND_LIFETIMES;
+extern const isc::log::MessageID DHCPSRV_CLIENT_CLASS_DEPRECATED;
extern const isc::log::MessageID DHCPSRV_CLOSE_DB;
extern const isc::log::MessageID DHCPSRV_DDNS_TTL_PERCENT_TOO_SMALL;
extern const isc::log::MessageID DHCPSRV_DHCP4O6_RECEIVED_BAD_PACKET;
diff --git a/src/lib/dhcpsrv/dhcpsrv_messages.mes b/src/lib/dhcpsrv/dhcpsrv_messages.mes
index cdb3a42c63..88c05fc78f 100644
--- a/src/lib/dhcpsrv/dhcpsrv_messages.mes
+++ b/src/lib/dhcpsrv/dhcpsrv_messages.mes
@@ -958,7 +958,7 @@ included in the message.
The database access string specified a database type (given in the
message) that is unknown to the software. This is a configuration error.
-% DHCPSRV_CLASS_WITH_ADDTIONAL_AND_LIFETIMES class: %1 has 'only-in-additional-list' true while specifying one or more lease life time values. Life time values will be ignored.
+% DHCPSRV_CLASS_WITH_ADDITIONAL_AND_LIFETIMES class: %1 has 'only-in-additional-list' true while specifying one or more lease life time values. Life time values will be ignored.
This warning is emitted whenever a class is configured with
'only-in-addition-list' true as well as specifying one or
more lease life time parameters (e.g. 'valid-lifetime',
@@ -966,3 +966,8 @@ more lease life time parameters (e.g. 'valid-lifetime',
are evaluated after lease assignment, thus parameters that would otherwise
impact lease life times will have no affect.
+% DHCPSRV_CLIENT_CLASS_DEPRECATED The parameter 'client-class' is deprecated. Use 'client-classes' list parameter instead
+This warning message is emitted when configuration parsing detects
+the use of the deprecated 'client-class' parameter. It has
+been replaced by 'client-classes'. Users should migrate
+to the new list parameter.
diff --git a/src/lib/dhcpsrv/network.cc b/src/lib/dhcpsrv/network.cc
index bb9511fffb..8ea291f6a4 100644
--- a/src/lib/dhcpsrv/network.cc
+++ b/src/lib/dhcpsrv/network.cc
@@ -67,18 +67,19 @@ Network::getRelayAddresses() const {
bool
Network::clientSupported(const isc::dhcp::ClientClasses& classes) const {
- if (client_class_.empty()) {
- // There is no class defined for this network, so we do
- // support everyone.
+ if (client_classes_.empty()) {
+ // Empty list for this network, so we support everyone.
return (true);
}
- return (classes.contains(client_class_));
+ return (client_classes_.intersects(classes));
}
void
Network::allowClientClass(const isc::dhcp::ClientClass& class_name) {
- client_class_ = class_name;
+ if (!client_classes_.contains(class_name)) {
+ client_classes_.insert(class_name);
+ }
}
void
@@ -88,11 +89,6 @@ Network::addAdditionalClass(const isc::dhcp::ClientClass& class_name) {
}
}
-const ClientClasses&
-Network::getAdditionalClasses() const {
- return (additional_classes_);
-}
-
Optional<IOAddress>
Network::getGlobalProperty(Optional<IOAddress> property,
const int global_index,
@@ -135,19 +131,15 @@ Network::toElement() const {
relay_map->set("ip-addresses", address_list);
map->set("relay", relay_map);
- // Set client-class
- if (!client_class_.unspecified()) {
- map->set("client-class", Element::create(client_class_.get()));
+ // Set client-classes
+ if (!client_classes_.empty()) {
+ map->set("client-classes", client_classes_.toElement());
}
// Set evaluate-additional-classes
- const ClientClasses& classes = getAdditionalClasses();
- if (!classes.empty()) {
- ElementPtr class_list = Element::createList();
- for (auto const& it : classes) {
- class_list->add(Element::create(it));
- }
- map->set("evaluate-additional-classes", class_list);
+ if (!additional_classes_.empty()) {
+ map->set("evaluate-additional-classes",
+ additional_classes_.toElement());
}
// T1, T2, and Valid are optional for SharedNetworks, and
diff --git a/src/lib/dhcpsrv/network.h b/src/lib/dhcpsrv/network.h
index fb934c4e27..a460b677cf 100644
--- a/src/lib/dhcpsrv/network.h
+++ b/src/lib/dhcpsrv/network.h
@@ -211,7 +211,7 @@ public:
/// @brief Constructor.
Network()
- : iface_name_(), client_class_(), t1_(), t2_(), valid_(),
+ : iface_name_(), client_classes_(), t1_(), t2_(), valid_(),
reservations_global_(false, true), reservations_in_subnet_(true, true),
reservations_out_of_pool_(false, true), cfg_option_(new CfgOption()),
calculate_tee_times_(), t1_percent_(), t2_percent_(),
@@ -327,8 +327,8 @@ public:
/// @return True if a relay with the given address is found, false otherwise
bool hasRelayAddress(const asiolink::IOAddress& address) const;
- /// @brief Checks whether this network supports client that belongs to
- /// specified classes.
+ /// @brief Checks whether this network supports a client that belongs to
+ /// the specified classes.
///
/// This method checks whether a client that belongs to given classes can
/// use this network. For example, if this class is reserved for client
@@ -336,45 +336,41 @@ public:
/// it is supported. On the other hand, client belonging to classes
/// "foobar" and "zyxxy" is not supported.
///
- /// @note: changed the planned white and black lists idea to a simple
- /// client class name.
- ///
/// @param client_classes list of all classes the client belongs to
/// @return true if client can be supported, false otherwise
virtual bool
clientSupported(const isc::dhcp::ClientClasses& client_classes) const;
- /// @brief Sets the supported class to class class_name
+ /// @brief Adds class clas_name to the allowed client classes list.
///
/// @param class_name client class to be supported by this network
void allowClientClass(const isc::dhcp::ClientClass& class_name);
+ /// @brief Returns the list of allowed client classes.
+ const ClientClasses& getClientClasses() const {
+ return (client_classes_);
+ }
+
+ /// @brief Returns the mutable list of allowed client classes.
+ ClientClasses& getMutableClientClasses() {
+ return (client_classes_);
+ }
+
/// @brief Adds class class_name to the additional classes list.
///
/// @param class_name client class to add
void addAdditionalClass(const isc::dhcp::ClientClass& class_name);
/// @brief Returns the additional classes list.
- const ClientClasses& getAdditionalClasses() const;
+ const ClientClasses& getAdditionalClasses() const {
+ return (additional_classes_);
+ }
/// @brief Returns the mutable additional classes list.
ClientClasses& getMutableAdditionalClasses() {
return (additional_classes_);
}
- /// @brief returns the client class
- ///
- /// @note The returned reference is only valid as long as the object
- /// returned it is valid.
- ///
- /// @param inheritance inheritance mode to be used.
- /// @return client class @ref client_class_
- util::Optional<ClientClass>
- getClientClass(const Inheritance& inheritance = Inheritance::ALL) const {
- return (getProperty<Network>(&Network::getClientClass, client_class_,
- inheritance));
- }
-
/// @brief Return valid-lifetime for addresses in that prefix
///
/// @param inheritance inheritance mode to be used.
@@ -1145,12 +1141,13 @@ protected:
/// See @ref RelayInfo for detailed description.
RelayInfo relay_;
- /// @brief Optional definition of a client class
+ /// @brief List of client classes allowed to use this network.
///
- /// If defined, only clients belonging to that class will be allowed to use
- /// this particular network. The default value for this is an empty string,
- /// which means that any client is allowed, regardless of its class.
- util::Optional<ClientClass> client_class_;
+ /// If not empty, only clients belonging to at least one of the classes
+ /// in this list will be allowed to use this particular network. By default
+ /// the list is empty which means that any client is allowed, regardless
+ /// of its class membership.
+ ClientClasses client_classes_;
/// @brief Additional classes
///
diff --git a/src/lib/dhcpsrv/parsers/base_network_parser.cc b/src/lib/dhcpsrv/parsers/base_network_parser.cc
index 85e97ebbf6..cc3bb5b45d 100644
--- a/src/lib/dhcpsrv/parsers/base_network_parser.cc
+++ b/src/lib/dhcpsrv/parsers/base_network_parser.cc
@@ -281,5 +281,42 @@ BaseNetworkParser::getAdditionalClassesElem(ConstElementPtr params,
}
}
+void
+BaseNetworkParser::getClientClassesElem(ConstElementPtr params,
+ ClassAdderFunc adder_func) {
+ // Try setting up client client classes.
+ ConstElementPtr class_elem = params->get("client-class");
+ ConstElementPtr class_list = params->get("client-classes");
+ if (class_elem) {
+ if (!class_list) {
+ LOG_WARN(dhcpsrv_logger, DHCPSRV_CLIENT_CLASS_DEPRECATED);
+ if (class_elem->getType() != Element::string) {
+ isc_throw(DhcpConfigError, "invalid class name (" << class_elem->getPosition() << ")");
+ }
+
+ if (!class_elem->stringValue().empty()) {
+ (adder_func)(class_elem->stringValue());
+ }
+ } else {
+ isc_throw(isc::dhcp::DhcpConfigError,
+ "cannot specify both 'client-class' and "
+ "'client-classes'. Use only the latter.");
+ }
+ }
+
+ if (class_list) {
+ const std::vector<data::ElementPtr>& classes = class_list->listValue();
+ for (auto const& cclass : classes) {
+ if ((cclass->getType() != Element::string) ||
+ cclass->stringValue().empty()) {
+ isc_throw(DhcpConfigError, "invalid class name (" << cclass->getPosition() << ")");
+ }
+
+ (adder_func)(cclass->stringValue());
+ }
+ }
+}
+
+
} // end of namespace isc::dhcp
} // end of namespace isc
diff --git a/src/lib/dhcpsrv/parsers/base_network_parser.h b/src/lib/dhcpsrv/parsers/base_network_parser.h
index c4e2a14b4e..f61cca2002 100644
--- a/src/lib/dhcpsrv/parsers/base_network_parser.h
+++ b/src/lib/dhcpsrv/parsers/base_network_parser.h
@@ -138,6 +138,16 @@ public:
/// @throw DhcpConfigError if both entries are present.
static void getAdditionalClassesElem(data::ConstElementPtr params,
ClassAdderFunc adder_func);
+
+ /// @brief Fetches the element for either 'client-classes' or deprecated
+ /// 'client-class'
+ ///
+ /// @param params configuration element tree to search.
+ /// @param adder_func function to add class names to an object's client class list.
+ /// @return Element referred to or an empty pointer.
+ /// @throw DhcpConfigError if both entries are present.
+ static void getClientClassesElem(data::ConstElementPtr params,
+ ClassAdderFunc adder_func);
};
} // end of namespace isc::dhcp
diff --git a/src/lib/dhcpsrv/parsers/dhcp_parsers.cc b/src/lib/dhcpsrv/parsers/dhcp_parsers.cc
index f6be7c794a..2d6e0631d5 100644
--- a/src/lib/dhcpsrv/parsers/dhcp_parsers.cc
+++ b/src/lib/dhcpsrv/parsers/dhcp_parsers.cc
@@ -523,6 +523,11 @@ PoolParser::parse(PoolStoragePtr pools,
}
}
+ // Setup client class list.
+ BaseNetworkParser::getClientClassesElem(pool_structure,
+ std::bind(&Pool::allowClientClass,
+ pool, ph::_1));
+
// Setup additional class list.
BaseNetworkParser::getAdditionalClassesElem(pool_structure,
std::bind(&Pool::addAdditionalClass,
@@ -890,6 +895,10 @@ Subnet4ConfigParser::initSubnet(data::ConstElementPtr params,
}
}
+ // Setup client class list.
+ getClientClassesElem(params, std::bind(&Network::allowClientClass,
+ subnet4, ph::_1));
+
// Setup additional class list.
getAdditionalClassesElem(params, std::bind(&Network::addAdditionalClass,
subnet4, ph::_1));
@@ -1167,6 +1176,11 @@ PdPoolParser::parse(PoolStoragePtr pools, ConstElementPtr pd_pool,
}
}
+ // Setup client class list.
+ BaseNetworkParser::getClientClassesElem(pd_pool,
+ std::bind(&Pool::allowClientClass,
+ pool_, ph::_1));
+
// Setup additional class list.
BaseNetworkParser::getAdditionalClassesElem(pd_pool,
std::bind(&Pool::addAdditionalClass,
@@ -1399,6 +1413,10 @@ Subnet6ConfigParser::initSubnet(data::ConstElementPtr params,
}
}
+ // Setup client class list.
+ getClientClassesElem(params, std::bind(&Network::allowClientClass,
+ subnet6, ph::_1));
+
// Setup additional class list.
getAdditionalClassesElem(params, std::bind(&Network::addAdditionalClass,
subnet6, ph::_1));
diff --git a/src/lib/dhcpsrv/parsers/shared_network_parser.cc b/src/lib/dhcpsrv/parsers/shared_network_parser.cc
index d9d8a0e7c0..100eef35e3 100644
--- a/src/lib/dhcpsrv/parsers/shared_network_parser.cc
+++ b/src/lib/dhcpsrv/parsers/shared_network_parser.cc
@@ -153,19 +153,17 @@ SharedNetwork4Parser::parse(const data::ConstElementPtr& shared_network_data,
}
}
- if (shared_network_data->contains("client-class")) {
- std::string client_class = getString(shared_network_data, "client-class");
- if (!client_class.empty()) {
- shared_network->allowClientClass(client_class);
- }
- }
-
ConstElementPtr user_context = shared_network_data->get("user-context");
if (user_context) {
shared_network->setContext(user_context);
}
// Setup additional class list.
+ getClientClassesElem(shared_network_data,
+ std::bind(&Network::allowClientClass,
+ shared_network, ph::_1));
+
+ // Setup additional class list.
getAdditionalClassesElem(shared_network_data,
std::bind(&Network::addAdditionalClass,
shared_network, ph::_1));
@@ -313,19 +311,17 @@ SharedNetwork6Parser::parse(const data::ConstElementPtr& shared_network_data,
parser->parse(cfg_option, json, encapsulate_options);
}
- if (shared_network_data->contains("client-class")) {
- std::string client_class = getString(shared_network_data, "client-class");
- if (!client_class.empty()) {
- shared_network->allowClientClass(client_class);
- }
- }
-
ConstElementPtr user_context = shared_network_data->get("user-context");
if (user_context) {
shared_network->setContext(user_context);
}
// Setup additional class list.
+ getClientClassesElem(shared_network_data,
+ std::bind(&Network::allowClientClass,
+ shared_network, ph::_1));
+
+ // Setup additional class list.
getAdditionalClassesElem(shared_network_data,
std::bind(&Network::addAdditionalClass,
shared_network, ph::_1));
diff --git a/src/lib/dhcpsrv/parsers/simple_parser4.cc b/src/lib/dhcpsrv/parsers/simple_parser4.cc
index 0319814249..b170be6fc5 100644
--- a/src/lib/dhcpsrv/parsers/simple_parser4.cc
+++ b/src/lib/dhcpsrv/parsers/simple_parser4.cc
@@ -223,6 +223,7 @@ const SimpleKeywords SimpleParser4::SUBNET4_PARAMETERS = {
{ "interface", Element::string },
{ "id", Element::integer },
{ "client-class", Element::string },
+ { "client-classes", Element::list },
{ "require-client-classes", Element::list },
{ "evaluate-additional-classes", Element::list },
{ "reservations", Element::list },
@@ -271,7 +272,6 @@ const SimpleKeywords SimpleParser4::SUBNET4_PARAMETERS = {
/// interface.
const SimpleDefaults SimpleParser4::SUBNET4_DEFAULTS = {
{ "interface", Element::string, "" },
- { "client-class", Element::string, "" },
{ "4o6-interface", Element::string, "" },
{ "4o6-interface-id", Element::string, "" },
{ "4o6-subnet", Element::string, "" },
@@ -291,7 +291,6 @@ const SimpleDefaults SimpleParser4::SHARED_SUBNET4_DEFAULTS = {
/// @brief This table defines default values for each IPv4 shared network.
const SimpleDefaults SimpleParser4::SHARED_NETWORK4_DEFAULTS = {
- { "client-class", Element::string, "" },
{ "interface", Element::string, "" }
};
@@ -331,6 +330,7 @@ const SimpleKeywords SimpleParser4::POOL4_PARAMETERS = {
{ "pool-id", Element::integer },
{ "option-data", Element::list },
{ "client-class", Element::string },
+ { "client-classes", Element::list },
{ "require-client-classes", Element::list },
{ "evaluate-additional-classes", Element::list },
{ "user-context", Element::map },
@@ -360,6 +360,7 @@ const SimpleKeywords SimpleParser4::SHARED_NETWORK4_PARAMETERS = {
{ "reservations-in-subnet", Element::boolean },
{ "reservations-out-of-pool", Element::boolean },
{ "client-class", Element::string },
+ { "client-classes", Element::list },
{ "require-client-classes", Element::list },
{ "evaluate-additional-classes", Element::list },
{ "valid-lifetime", Element::integer },
diff --git a/src/lib/dhcpsrv/parsers/simple_parser6.cc b/src/lib/dhcpsrv/parsers/simple_parser6.cc
index 89fedf0a81..b158afdf37 100644
--- a/src/lib/dhcpsrv/parsers/simple_parser6.cc
+++ b/src/lib/dhcpsrv/parsers/simple_parser6.cc
@@ -53,7 +53,8 @@ const SimpleKeywords SimpleParser6::GLOBAL6_PARAMETERS = {
{ "mac-sources", Element::list },
{ "relay-supplied-options", Element::list },
{ "host-reservation-identifiers", Element::list },
- { "client-classes", Element::list },
+ { "client-class", Element::string },
+ { "client-classes", Element::string },
{ "option-def", Element::list },
{ "option-data", Element::list },
{ "hooks-libraries", Element::list },
@@ -223,6 +224,7 @@ const SimpleKeywords SimpleParser6::SUBNET6_PARAMETERS = {
{ "id", Element::integer },
{ "rapid-commit", Element::boolean },
{ "client-class", Element::string },
+ { "client-classes", Element::list },
{ "require-client-classes", Element::list },
{ "evaluate-additional-classes", Element::list },
{ "reservations", Element::list },
@@ -262,7 +264,6 @@ const SimpleKeywords SimpleParser6::SUBNET6_PARAMETERS = {
/// defined on global level.
const SimpleDefaults SimpleParser6::SUBNET6_DEFAULTS = {
{ "interface", Element::string, "" },
- { "client-class", Element::string, "" },
{ "rapid-commit", Element::boolean, "false" }, // rapid-commit disabled by default
{ "interface-id", Element::string, "" }
};
@@ -277,7 +278,6 @@ const SimpleDefaults SimpleParser6::SHARED_SUBNET6_DEFAULTS = {
/// @brief This table defines default values for each IPv6 shared network.
const SimpleDefaults SimpleParser6::SHARED_NETWORK6_DEFAULTS = {
- { "client-class", Element::string, "" },
{ "interface", Element::string, "" },
{ "interface-id", Element::string, "" },
{ "rapid-commit", Element::boolean, "false" } // rapid-commit disabled by default
@@ -322,6 +322,7 @@ const SimpleKeywords SimpleParser6::POOL6_PARAMETERS = {
{ "pool-id", Element::integer },
{ "option-data", Element::list },
{ "client-class", Element::string },
+ { "client-classes", Element::list },
{ "require-client-classes", Element::list },
{ "evaluate-additional-classes", Element::list },
{ "user-context", Element::map },
@@ -341,6 +342,7 @@ const SimpleKeywords SimpleParser6::PD_POOL6_PARAMETERS = {
{ "pool-id", Element::integer },
{ "option-data", Element::list },
{ "client-class", Element::string },
+ { "client-classes", Element::list },
{ "require-client-classes", Element::list },
{ "evaluate-additional-classes", Element::list },
{ "excluded-prefix", Element::string },
@@ -368,6 +370,7 @@ const SimpleKeywords SimpleParser6::SHARED_NETWORK6_PARAMETERS = {
{ "reservations-in-subnet", Element::boolean },
{ "reservations-out-of-pool", Element::boolean },
{ "client-class", Element::string },
+ { "client-classes", Element::list },
{ "require-client-classes", Element::list },
{ "evaluate-additional-classes", Element::list },
{ "preferred-lifetime", Element::integer },
diff --git a/src/lib/dhcpsrv/pool.cc b/src/lib/dhcpsrv/pool.cc
index bb4da02483..2c80084e6e 100644
--- a/src/lib/dhcpsrv/pool.cc
+++ b/src/lib/dhcpsrv/pool.cc
@@ -21,19 +21,31 @@ namespace dhcp {
Pool::Pool(Lease::Type type, const isc::asiolink::IOAddress& first,
const isc::asiolink::IOAddress& last)
: id_(0), first_(first), last_(last), type_(type), capacity_(0),
- cfg_option_(new CfgOption()), client_class_("") {
+ cfg_option_(new CfgOption()) {
}
-bool Pool::inRange(const isc::asiolink::IOAddress& addr) const {
+bool
+Pool::inRange(const isc::asiolink::IOAddress& addr) const {
return (first_ <= addr && addr <= last_);
}
-bool Pool::clientSupported(const ClientClasses& classes) const {
- return (client_class_.empty() || classes.contains(client_class_));
+bool
+Pool::clientSupported(const ClientClasses& classes) const {
+ return (client_classes_.empty() || client_classes_.intersects(classes));
}
-void Pool::allowClientClass(const ClientClass& class_name) {
- client_class_ = class_name;
+void
+Pool::allowClientClass(const ClientClass& class_name) {
+ if (!client_classes_.contains(class_name)) {
+ client_classes_.insert(class_name);
+ }
+}
+
+void
+Pool::addAdditionalClass(const isc::dhcp::ClientClass& class_name) {
+ if (!additional_classes_.contains(class_name)) {
+ additional_classes_.insert(class_name);
+ }
}
std::string
@@ -115,20 +127,15 @@ Pool::toElement() const {
ConstCfgOptionPtr opts = getCfgOption();
map->set("option-data", opts->toElement());
- // Set client-class
- const ClientClass& cclass = getClientClass();
- if (!cclass.empty()) {
- map->set("client-class", Element::create(cclass));
+ // Set client-classes
+ if (!client_classes_.empty()) {
+ map->set("client-classes", client_classes_.toElement());
}
// Set evaluate-additional-classes
- const ClientClasses& classes = getAdditionalClasses();
- if (!classes.empty()) {
- ElementPtr class_list = Element::createList();
- for (auto const& it : classes) {
- class_list->add(Element::create(it));
- }
- map->set("evaluate-additional-classes", class_list);
+ if (!additional_classes_.empty()) {
+ map->set("evaluate-additional-classes",
+ additional_classes_.toElement());
}
if (id_) {
diff --git a/src/lib/dhcpsrv/pool.h b/src/lib/dhcpsrv/pool.h
index 85301790c3..7d2ad92090 100644
--- a/src/lib/dhcpsrv/pool.h
+++ b/src/lib/dhcpsrv/pool.h
@@ -114,35 +114,29 @@ public:
/// @brief Checks whether this pool supports client that belongs to
/// specified classes.
///
- /// @todo: currently doing the same as network which needs improving.
- ///
/// @param client_classes list of all classes the client belongs to
/// @return true if client can be supported, false otherwise
bool clientSupported(const ClientClasses& client_classes) const;
- /// @brief Sets the supported class to class class_name
+ /// @brief Adds class clas_name to the allowed client classes list.
///
- /// @param class_name client class to be supported by this pool
- void allowClientClass(const ClientClass& class_name);
+ /// @param class_name client class to be supported by this network
+ void allowClientClass(const isc::dhcp::ClientClass& class_name);
- /// @brief returns the client class
- ///
- /// @note The returned reference is only valid as long as the object
- /// returned is valid.
- ///
- /// @return client class @ref client_class_
- const ClientClass& getClientClass() const {
- return (client_class_);
+ /// @brief Returns the list of allowed client classes.
+ const ClientClasses& getClientClasses() const {
+ return (client_classes_);
+ }
+
+ /// @brief Returns the mutable list of allowed client classes.
+ ClientClasses& getMutableClientClasses() {
+ return (client_classes_);
}
/// @brief Adds class class_name to the additional classes list.
///
/// @param class_name client class to add
- void addAdditionalClass(const ClientClass& class_name) {
- if (!additional_classes_.contains(class_name)) {
- additional_classes_.insert(class_name);
- }
- }
+ void addAdditionalClass(const ClientClass& class_name);
/// @brief Returns the additional classes list.
const ClientClasses& getAdditionalClasses() const {
@@ -216,10 +210,13 @@ protected:
/// @brief Pointer to the option data configuration for this pool.
CfgOptionPtr cfg_option_;
- /// @brief Optional definition of a client class
+ /// @brief List of client classes allowed to use this pool.
///
- /// @ref Network::client_class_
- ClientClass client_class_;
+ /// If not empty, only clients belonging to at least one of the classes
+ /// in this list will be allowed to use this particular pool. By default
+ /// the list is empty which means that any client is allowed, regardless
+ /// of its class membership.
+ ClientClasses client_classes_;
/// @brief Additional classes
///
diff --git a/src/lib/dhcpsrv/shared_network.cc b/src/lib/dhcpsrv/shared_network.cc
index eedfe41b32..acf3ee5666 100644
--- a/src/lib/dhcpsrv/shared_network.cc
+++ b/src/lib/dhcpsrv/shared_network.cc
@@ -308,7 +308,7 @@ public:
if (preferred_subnet == s) {
continue;
}
- if (s->getClientClass().get() != selected_subnet->getClientClass().get()) {
+ if (s->getClientClasses() != selected_subnet->getClientClasses()) {
continue;
}
auto current_subnet_state = s->getAllocationState(lease_type);
diff --git a/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc b/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc
index 6d56ef1951..43b5be363e 100644
--- a/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc
+++ b/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc
@@ -1082,6 +1082,7 @@ TEST(CfgSubnets4Test, unparseSubnet) {
Subnet4Ptr subnet3(new Subnet4(IOAddress("192.0.2.128"), 26, 1, 2, 3, 125));
subnet1->allowClientClass("foo");
+ subnet1->allowClientClass("bar");
subnet1->setT1Percent(0.45);
subnet1->setT2Percent(0.70);
@@ -1141,7 +1142,7 @@ TEST(CfgSubnets4Test, unparseSubnet) {
" \"valid-lifetime\": 3,\n"
" \"min-valid-lifetime\": 3,\n"
" \"max-valid-lifetime\": 3,\n"
- " \"client-class\": \"foo\",\n"
+ " \"client-classes\": [ \"foo\", \"bar\" ],\n"
" \"4o6-interface\": \"\",\n"
" \"4o6-interface-id\": \"\",\n"
" \"4o6-subnet\": \"\",\n"
@@ -1253,7 +1254,7 @@ TEST(CfgSubnets4Test, unparsePool) {
" \"option-data\": [ ],\n"
" \"pool\": \"192.0.2.64/26\",\n"
" \"user-context\": { \"foo\": \"bar\" },\n"
- " \"client-class\": \"bar\",\n"
+ " \"client-classes\": [ \"bar\" ],\n"
" \"evaluate-additional-classes\": [ \"foo\" ]\n"
" }\n"
" ]\n"
@@ -1426,7 +1427,7 @@ TEST(CfgSubnets4Test, teeTimePercentValidation) {
" \"next-server\": \"\", \n"
" \"server-hostname\": \"\", \n"
" \"boot-file-name\": \"\", \n"
- " \"client-class\": \"\", \n"
+ " \"client-classes\": [] , \n"
" \"evaluate-additional-classes\": [] \n,"
" \"reservations-global\": false, \n"
" \"reservations-in-subnet\": true, \n"
@@ -1495,8 +1496,8 @@ TEST(CfgSubnets4Test, validLifetimeValidation) {
" \"next-server\": \"\", \n"
" \"server-hostname\": \"\", \n"
" \"boot-file-name\": \"\", \n"
- " \"client-class\": \"\", \n"
- " \"evaluate-additional-classes\": [] \n,"
+ " \"client-classes\": [], \n"
+ " \"evaluate-additional-classes\": [], \n"
" \"reservations-global\": false, \n"
" \"reservations-in-subnet\": true, \n"
" \"reservations-out-of-pool\": false, \n"
@@ -1757,8 +1758,8 @@ TEST(CfgSubnets4Test, hostnameSanitizierValidation) {
" \"next-server\": \"\", \n"
" \"server-hostname\": \"\", \n"
" \"boot-file-name\": \"\", \n"
- " \"client-class\": \"\", \n"
- " \"evaluate-additional-classes\": [] \n,"
+ " \"client-classes\": [], \n"
+ " \"evaluate-additional-classes\": [], \n"
" \"reservations-global\": false, \n"
" \"reservations-in-subnet\": true, \n"
" \"reservations-out-of-pool\": false, \n"
@@ -1837,8 +1838,8 @@ TEST(CfgSubnets4Test, cacheParamValidation) {
" \"next-server\": \"\", \n"
" \"server-hostname\": \"\", \n"
" \"boot-file-name\": \"\", \n"
- " \"client-class\": \"\", \n"
- " \"evaluate-additional-classes\": [] \n,"
+ " \"client-classes\": [], \n"
+ " \"evaluate-additional-classes\": [], \n"
" \"reservations-global\": false, \n"
" \"reservations-in-subnet\": true, \n"
" \"reservations-out-of-pool\": false, \n"
diff --git a/src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc b/src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc
index 4cc07fa0ab..72fc840c73 100644
--- a/src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc
+++ b/src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc
@@ -357,7 +357,9 @@ TEST(CfgSubnets6Test, selectSubnetByNetworkRelayAddress) {
EXPECT_EQ(subnet3, cfg.selectSubnet(selector));
// Modify the client classes associated with the first two subnets.
+ subnet1->getMutableClientClasses().clear();
subnet1->allowClientClass("subnet1");
+ subnet2->getMutableClientClasses().clear();
subnet2->allowClientClass("subnet2");
// This time the non-matching classes should prevent selection.
@@ -677,6 +679,7 @@ TEST(CfgSubnets6Test, unparseSubnet) {
OptionPtr ifaceid = generateInterfaceId("relay.eth0");
subnet1->setInterfaceId(ifaceid);
subnet1->allowClientClass("foo");
+ subnet1->allowClientClass("bar");
subnet1->setT1Percent(0.45);
subnet1->setT2Percent(0.70);
@@ -737,7 +740,7 @@ TEST(CfgSubnets6Test, unparseSubnet) {
" \"valid-lifetime\": 4,\n"
" \"min-valid-lifetime\": 4,\n"
" \"max-valid-lifetime\": 4,\n"
- " \"client-class\": \"foo\",\n"
+ " \"client-classes\": [ \"foo\", \"bar\" ],\n"
" \"pools\": [ ],\n"
" \"pd-pools\": [ ],\n"
" \"option-data\": [ ],\n"
@@ -846,7 +849,7 @@ TEST(CfgSubnets6Test, unparsePool) {
" \"pool\": \"2001:db8:1:1::/64\",\n"
" \"user-context\": { \"foo\": \"bar\" },\n"
" \"option-data\": [ ],\n"
- " \"client-class\": \"bar\",\n"
+ " \"client-classes\": [ \"bar\" ],\n"
" \"evaluate-additional-classes\": [ \"foo\" ]\n"
" }\n"
" ],\n"
@@ -909,7 +912,7 @@ TEST(CfgSubnets6Test, unparsePdPool) {
" \"excluded-prefix\": \"2001:db8:3::\",\n"
" \"excluded-prefix-len\": 64,\n"
" \"option-data\": [ ],\n"
- " \"client-class\": \"bar\"\n"
+ " \"client-classes\": [ \"bar\" ]\n"
" }\n"
" ],\n"
" \"option-data\": [ ]\n"
@@ -1227,8 +1230,8 @@ TEST(CfgSubnets6Test, teeTimePercentValidation) {
" \"renew-timer\": 100, \n"
" \"rebind-timer\": 200, \n"
" \"valid-lifetime\": 300, \n"
- " \"client-class\": \"\", \n"
- " \"evaluate-additional-classes\": [] \n,"
+ " \"client-classes\": [], \n"
+ " \"evaluate-additional-classes\": [], \n"
" \"reservations-global\": false, \n"
" \"reservations-in-subnet\": true, \n"
" \"reservations-out-of-pool\": false \n"
@@ -1292,8 +1295,8 @@ TEST(CfgSubnets6Test, preferredLifetimeValidation) {
" \"renew-timer\": 100, \n"
" \"rebind-timer\": 200, \n"
" \"valid-lifetime\": 300, \n"
- " \"client-class\": \"\", \n"
- " \"evaluate-additional-classes\": [] \n,"
+ " \"client-classes\": [], \n"
+ " \"evaluate-additional-classes\": [], \n"
" \"reservations-global\": false, \n"
" \"reservations-in-subnet\": true, \n"
" \"reservations-out-of-pool\": false \n"
@@ -1547,8 +1550,8 @@ TEST(CfgSubnets6Test, hostnameSanitizierValidation) {
" \"renew-timer\": 100, \n"
" \"rebind-timer\": 200, \n"
" \"valid-lifetime\": 300, \n"
- " \"client-class\": \"\", \n"
- " \"evaluate-additional-classes\": [] \n,"
+ " \"client-classes\": [], \n"
+ " \"evaluate-additional-classes\": [], \n"
" \"reservations-global\": false, \n"
" \"reservations-in-subnet\": true, \n"
" \"reservations-out-of-pool\": false \n"
@@ -1619,8 +1622,8 @@ TEST(CfgSubnets6Test, cacheParamValidation) {
" \"renew-timer\": 100, \n"
" \"rebind-timer\": 200, \n"
" \"valid-lifetime\": 300, \n"
- " \"client-class\": \"\", \n"
- " \"evaluate-additional-classes\": [] \n,"
+ " \"client-classes\": [], \n"
+ " \"evaluate-additional-classes\": [], \n"
" \"reservations-global\": false, \n"
" \"reservations-in-subnet\": true, \n"
" \"reservations-out-of-pool\": false \n"
diff --git a/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc b/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc
index 6415e197a2..4b46de77df 100644
--- a/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc
+++ b/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc
@@ -2898,8 +2898,7 @@ TEST_F(ParseConfigTest, defaultSubnet4) {
EXPECT_TRUE(subnet->getIface().unspecified());
EXPECT_TRUE(subnet->getIface().empty());
- EXPECT_TRUE(subnet->getClientClass().unspecified());
- EXPECT_TRUE(subnet->getClientClass().empty());
+ EXPECT_TRUE(subnet->getClientClasses().empty());
EXPECT_TRUE(subnet->getValid().unspecified());
EXPECT_EQ(0, subnet->getValid().get());
@@ -3017,8 +3016,7 @@ TEST_F(ParseConfigTest, defaultSubnet6) {
EXPECT_TRUE(subnet->getIface().unspecified());
EXPECT_TRUE(subnet->getIface().empty());
- EXPECT_TRUE(subnet->getClientClass().unspecified());
- EXPECT_TRUE(subnet->getClientClass().empty());
+ EXPECT_TRUE(subnet->getClientClasses().empty());
EXPECT_TRUE(subnet->getValid().unspecified());
EXPECT_EQ(0, subnet->getValid().get());
@@ -3122,8 +3120,7 @@ TEST_F(ParseConfigTest, defaultSharedNetwork4) {
EXPECT_TRUE(network->getIface().unspecified());
EXPECT_TRUE(network->getIface().empty());
- EXPECT_TRUE(network->getClientClass().unspecified());
- EXPECT_TRUE(network->getClientClass().empty());
+ EXPECT_TRUE(network->getClientClasses().empty());
EXPECT_TRUE(network->getValid().unspecified());
EXPECT_EQ(0, network->getValid().get());
@@ -3215,8 +3212,7 @@ TEST_F(ParseConfigTest, defaultSharedNetwork6) {
EXPECT_TRUE(network->getIface().unspecified());
EXPECT_TRUE(network->getIface().empty());
- EXPECT_TRUE(network->getClientClass().unspecified());
- EXPECT_TRUE(network->getClientClass().empty());
+ EXPECT_TRUE(network->getClientClasses().empty());
EXPECT_TRUE(network->getValid().unspecified());
EXPECT_EQ(0, network->getValid().get());
@@ -3916,4 +3912,207 @@ TEST_F(DhcpParserTest, deprecatedRequireClientClassesPool6) {
" 'evaluate-additional-classes'. Use only the latter.");
}
+// Verify that deprecated client-class is handled properly
+// by Subnet4 parser.
+TEST_F(DhcpParserTest, deprecatedClientClassSubnet4) {
+ // Valid empty entry.
+ std::string config =
+ R"^({
+ "id": 1,
+ "subnet": "192.0.2.0/24",
+ "client-class": ""
+ })^";
+
+ ElementPtr config_element = Element::fromJSON(config);
+
+ // Parse configuration specified above.
+ Subnet4ConfigParser parser(AF_INET);
+ Subnet4Ptr subnet;
+
+ ASSERT_NO_THROW(subnet = parser.parse(config_element));
+ ASSERT_TRUE(subnet);
+
+ EXPECT_EQ(subnet->getClientClasses().size(), 0);
+
+ // Valid entry.
+ config =
+ R"^({
+ "id": 1,
+ "subnet": "192.0.2.0/24",
+ "client-class": "one"
+ })^";
+
+ config_element = Element::fromJSON(config);
+
+ // Parse configuration specified above.
+ ASSERT_NO_THROW(subnet = parser.parse(config_element));
+ ASSERT_TRUE(subnet);
+
+ const auto cclasses = subnet->getClientClasses();
+ EXPECT_EQ(cclasses.size(), 1);
+ auto cclass = cclasses.begin();
+ EXPECT_EQ(*cclass, "one");
+
+ // Invalid entry specifies both parameters.
+ config =
+ R"^({
+ "id": 1,
+ "subnet": "192.0.2.0/24",
+ "client-class": "one",
+ "client-classes": [ "one", "two" ]
+ })^";
+
+ config_element = Element::fromJSON(config);
+
+ // Should throw a complaint.
+ ASSERT_THROW_MSG(parser.parse(config_element),
+ DhcpConfigError,
+ "subnet configuration failed: "
+ "cannot specify both 'client-class' and"
+ " 'client-classes'. Use only the latter.");
+}
+
+// Verify that deprecated client-class is handled properly
+// by Subnet6 parser.
+TEST_F(DhcpParserTest, deprecatedClientClassSubnet6) {
+ // Valid empty entry.
+ std::string config =
+ R"^({
+ "id": 1,
+ "subnet": "2001:db8::/64",
+ "client-class": ""
+ })^";
+
+ ElementPtr config_element = Element::fromJSON(config);
+
+ // Parse configuration specified above.
+ Subnet6ConfigParser parser(AF_INET);
+ Subnet6Ptr subnet;
+
+ ASSERT_NO_THROW(subnet = parser.parse(config_element));
+ ASSERT_TRUE(subnet);
+
+ EXPECT_EQ(subnet->getClientClasses().size(), 0);
+
+ // Valid entry.
+ config =
+ R"^({
+ "id": 1,
+ "subnet": "2001:db8::/64",
+ "client-class": "one"
+ })^";
+
+ config_element = Element::fromJSON(config);
+
+ // Parse configuration specified above.
+ ASSERT_NO_THROW(subnet = parser.parse(config_element));
+ ASSERT_TRUE(subnet);
+
+ const auto cclasses = subnet->getClientClasses();
+ EXPECT_EQ(cclasses.size(), 1);
+ auto cclass = cclasses.begin();
+ EXPECT_EQ(*cclass, "one");
+
+ // Invalid entry specifies both parameters.
+ config =
+ R"^({
+ "id": 1,
+ "subnet": "2001:db8::/64",
+ "client-class": "one",
+ "client-classes": [ "one", "two" ]
+ })^";
+
+ config_element = Element::fromJSON(config);
+
+ // Should throw a complaint.
+ ASSERT_THROW_MSG(parser.parse(config_element),
+ DhcpConfigError,
+ "subnet configuration failed: "
+ "cannot specify both 'client-class' and"
+ " 'client-classes'. Use only the latter.");
+}
+
+// Verify that deprecated client-class is handled properly
+// by Pool4 parser.
+TEST_F(DhcpParserTest, deprecatedClientClassPool4) {
+ // Valid entry.
+ std::string config =
+ R"^({
+ "pool": "192.0.2.0/24",
+ "client-class": "one"
+ })^";
+
+ ElementPtr config_element = Element::fromJSON(config);
+
+ // Parse configuration specified above.
+ Pool4Parser parser;
+ PoolStoragePtr pools(new PoolStorage());
+
+ ASSERT_NO_THROW(parser.parse(pools, config_element, AF_INET));
+ EXPECT_EQ(1, pools->size());
+
+ const auto cclasses = (*pools)[0]->getClientClasses();
+ EXPECT_EQ(cclasses.size(), 1);
+ auto cclass = cclasses.begin();
+ EXPECT_EQ(*cclass, "one");
+
+ // Invalid entry specifies both parameters.
+ config =
+ R"^({
+ "pool": "192.0.2.0/24",
+ "client-class": "one",
+ "client-classes": [ "one", "two" ]
+ })^";
+
+ config_element = Element::fromJSON(config);
+
+ // Should throw a complaint.
+ ASSERT_THROW_MSG(parser.parse(pools, config_element, AF_INET),
+ DhcpConfigError,
+ "cannot specify both 'client-class' and"
+ " 'client-classes'. Use only the latter.");
+}
+
+// Verify that deprecated client-class is handled properly
+// by Pool6 parser. We only test TYPE_NA and as the same code is
+// used for either v6 pool type.
+TEST_F(DhcpParserTest, deprecatedClientClassPool6) {
+ // Valid entry.
+ std::string config =
+ R"^({
+ "pool": "2001:db8::/64",
+ "client-class": "one"
+ })^";
+
+ ElementPtr config_element = Element::fromJSON(config);
+
+ // Parse configuration specified above.
+ Pool6Parser parser;
+ PoolStoragePtr pools(new PoolStorage());
+
+ ASSERT_NO_THROW(parser.parse(pools, config_element, AF_INET6, Lease::TYPE_NA));
+ EXPECT_EQ(1, pools->size());
+
+ const auto cclasses = (*pools)[0]->getClientClasses();
+ EXPECT_EQ(cclasses.size(), 1);
+ auto cclass = cclasses.begin();
+ EXPECT_EQ(*cclass, "one");
+
+ // Invalid entry specifies both parameters.
+ config =
+ R"^({
+ "pool": "2001:db8::/64",
+ "client-class": "one",
+ "client-classes": [ "one", "two" ]
+ })^";
+
+ config_element = Element::fromJSON(config);
+
+ // Should throw a complaint.
+ ASSERT_THROW_MSG(parser.parse(pools, config_element, AF_INET6, Lease::TYPE_NA),
+ DhcpConfigError,
+ "cannot specify both 'client-class' and"
+ " 'client-classes'. Use only the latter.");
+}
+
} // Anonymous namespace
diff --git a/src/lib/dhcpsrv/tests/network_unittest.cc b/src/lib/dhcpsrv/tests/network_unittest.cc
index a1b57743aa..f0d2ac6801 100644
--- a/src/lib/dhcpsrv/tests/network_unittest.cc
+++ b/src/lib/dhcpsrv/tests/network_unittest.cc
@@ -186,13 +186,6 @@ TEST_F(NetworkTest, inheritanceSupport4) {
// For each parameter for which inheritance is supported run
// the test that checks if the values are inherited properly.
-
- {
- SCOPED_TRACE("client_class");
- testNetworkInheritance<TestNetwork>(&Network::getClientClass,
- &Network::allowClientClass,
- "n", "g", false);
- }
{
SCOPED_TRACE("valid-lifetime");
testNetworkInheritance<TestNetwork>(&Network::getValid, &Network::setValid,
diff --git a/src/lib/dhcpsrv/tests/pool_unittest.cc b/src/lib/dhcpsrv/tests/pool_unittest.cc
index 548d36bba8..a3424a3fd9 100644
--- a/src/lib/dhcpsrv/tests/pool_unittest.cc
+++ b/src/lib/dhcpsrv/tests/pool_unittest.cc
@@ -196,8 +196,8 @@ TEST(Pool4Test, userContext) {
EXPECT_EQ(ctx->str(), pool->getContext()->str());
}
-// This test checks that handling for client-class is valid.
-TEST(Pool4Test, clientClass) {
+// This test checks that handling for client-classes is valid.
+TEST(Pool4Test, clientClasses) {
// Create a pool.
Pool4Ptr pool(new Pool4(IOAddress("192.0.2.0"),
IOAddress("192.0.2.255")));
@@ -220,7 +220,7 @@ TEST(Pool4Test, clientClass) {
three_classes.insert("baz");
// No class restrictions defined, any client should be supported
- EXPECT_TRUE(pool->getClientClass().empty());
+ EXPECT_TRUE(pool->getClientClasses().empty());
EXPECT_TRUE(pool->clientSupported(no_class));
EXPECT_TRUE(pool->clientSupported(foo_class));
EXPECT_TRUE(pool->clientSupported(bar_class));
@@ -228,7 +228,7 @@ TEST(Pool4Test, clientClass) {
// Let's allow only clients belonging to "bar" class.
pool->allowClientClass("bar");
- EXPECT_EQ("bar", pool->getClientClass());
+ EXPECT_TRUE(pool->getClientClasses().contains("bar"));
EXPECT_FALSE(pool->clientSupported(no_class));
EXPECT_FALSE(pool->clientSupported(foo_class));
@@ -624,7 +624,7 @@ TEST(Pool6Test, clientClass) {
three_classes.insert("baz");
// No class restrictions defined, any client should be supported
- EXPECT_TRUE(pool.getClientClass().empty());
+ EXPECT_TRUE(pool.getClientClasses().empty());
EXPECT_TRUE(pool.clientSupported(no_class));
EXPECT_TRUE(pool.clientSupported(foo_class));
EXPECT_TRUE(pool.clientSupported(bar_class));
@@ -632,7 +632,7 @@ TEST(Pool6Test, clientClass) {
// Let's allow only clients belonging to "bar" class.
pool.allowClientClass("bar");
- EXPECT_EQ("bar", pool.getClientClass());
+ EXPECT_TRUE(pool.getClientClasses().contains("bar"));
EXPECT_FALSE(pool.clientSupported(no_class));
EXPECT_FALSE(pool.clientSupported(foo_class));
diff --git a/src/lib/dhcpsrv/tests/shared_network_parser_unittest.cc b/src/lib/dhcpsrv/tests/shared_network_parser_unittest.cc
index adb72e1ad0..8474fe3fc1 100644
--- a/src/lib/dhcpsrv/tests/shared_network_parser_unittest.cc
+++ b/src/lib/dhcpsrv/tests/shared_network_parser_unittest.cc
@@ -254,7 +254,7 @@ TEST_F(SharedNetwork4ParserTest, parse) {
// Check basic parameters.
EXPECT_TRUE(network->getAuthoritative());
- EXPECT_EQ("srv1", network->getClientClass().get());
+ EXPECT_TRUE(network->getClientClasses().contains("srv1"));
EXPECT_EQ("bird", network->getName());
EXPECT_EQ("eth1961", network->getIface().get());
EXPECT_EQ(99, network->getT1().get());
@@ -369,7 +369,7 @@ TEST_F(SharedNetwork4ParserTest, clientClassMatchClientIdAuthoritative) {
network = parser.parse(config_element);
ASSERT_TRUE(network);
- EXPECT_EQ("alpha", network->getClientClass().get());
+ EXPECT_TRUE(network->getClientClasses().contains("alpha"));
EXPECT_FALSE(network->getMatchClientId());
@@ -669,7 +669,7 @@ TEST_F(SharedNetwork6ParserTest, parse) {
ASSERT_TRUE(network);
// Check basic parameters.
- EXPECT_EQ("srv1", network->getClientClass().get());
+ EXPECT_TRUE(network->getClientClasses().contains("srv1"));
EXPECT_EQ("bird", network->getName());
EXPECT_EQ("eth1961", network->getIface().get());
EXPECT_EQ(211, network->getPreferred().get());
@@ -867,7 +867,7 @@ TEST_F(SharedNetwork6ParserTest, clientClass) {
network = parser.parse(config_element);
ASSERT_TRUE(network);
- EXPECT_EQ("alpha", network->getClientClass().get());
+ EXPECT_TRUE(network->getClientClasses().contains("alpha"));
}
// This test verifies that it's possible to specify evaluate-additional-classes
@@ -1087,7 +1087,6 @@ TEST_F(SharedNetwork4ParserTest, deprecatedRequireClientClasses) {
" (<string>:1:2)");
}
-
// Verify that deprecated require-client-classes is handled properly
// by v6 parser.
TEST_F(SharedNetwork6ParserTest, deprecatedRequireClientClasses) {
@@ -1095,8 +1094,7 @@ TEST_F(SharedNetwork6ParserTest, deprecatedRequireClientClasses) {
std::string config =
R"^({
"name": "foo",
- "require-client-classes": [ "one", "two" ]
- })^";
+ "require-client-classes": [ "one", "two" ] })^";
ElementPtr config_element = Element::fromJSON(config);
@@ -1132,4 +1130,89 @@ TEST_F(SharedNetwork6ParserTest, deprecatedRequireClientClasses) {
" (<string>:1:2)");
}
+// Verify that deprecated client-class is handled properly
+// by v4 parser.
+TEST_F(SharedNetwork4ParserTest, deprecatedClientClass) {
+ // Valid entry.
+ std::string config =
+ R"^({
+ "name": "foo",
+ "client-class": "one"
+ })^";
+
+ ElementPtr config_element = Element::fromJSON(config);
+
+ // Parse configuration specified above.
+ SharedNetwork4Parser parser;
+ SharedNetwork4Ptr network;
+
+ ASSERT_NO_THROW(network = parser.parse(config_element));
+ ASSERT_TRUE(network);
+
+ const auto cclasses = network->getClientClasses();
+ EXPECT_EQ(cclasses.size(), 1);
+ auto cclass = cclasses.begin();
+ EXPECT_EQ(*cclass, "one");
+
+ // Invalid entry specifies both parameters.
+ config =
+ R"^({
+ "name": "foo",
+ "client-class": "one",
+ "client-classes": [ "one", "two" ]
+ })^";
+
+ config_element = Element::fromJSON(config);
+
+ // Should throw a complaint.
+ ASSERT_THROW_MSG(parser.parse(config_element),
+ DhcpConfigError,
+ "cannot specify both 'client-class' and"
+ " 'client-classes'. Use only the latter."
+ " (<string>:1:2)");
+}
+
+// Verify that deprecated client-class is handled properly
+// by v6 parser.
+TEST_F(SharedNetwork6ParserTest, deprecatedClientClass) {
+ // Valid entry.
+ std::string config =
+ R"^({
+ "name": "foo",
+ "client-class": "one"
+ })^";
+
+ ElementPtr config_element = Element::fromJSON(config);
+
+ // Parse configuration specified above.
+ SharedNetwork6Parser parser;
+ SharedNetwork6Ptr network;
+
+ ASSERT_NO_THROW(network = parser.parse(config_element));
+ ASSERT_TRUE(network);
+
+ const auto cclasses = network->getClientClasses();
+ EXPECT_EQ(cclasses.size(), 1);
+ auto cclass = cclasses.begin();
+ EXPECT_EQ(*cclass, "one");
+
+ // Invalid entry specifies both parameters.
+ config =
+ R"^({
+ "name": "foo",
+ "client-class": "one",
+ "client-classes": [ "one", "two" ]
+ })^";
+
+ config_element = Element::fromJSON(config);
+
+ // Should throw a complaint.
+ ASSERT_THROW_MSG(parser.parse(config_element),
+ DhcpConfigError,
+ "cannot specify both 'client-class' and"
+ " 'client-classes'. Use only the latter."
+ " (<string>:1:2)");
+}
+
+
} // end of anonymous namespace
diff --git a/src/lib/dhcpsrv/tests/shared_network_unittest.cc b/src/lib/dhcpsrv/tests/shared_network_unittest.cc
index 2cb76b289c..2006b1c474 100644
--- a/src/lib/dhcpsrv/tests/shared_network_unittest.cc
+++ b/src/lib/dhcpsrv/tests/shared_network_unittest.cc
@@ -77,8 +77,7 @@ TEST(SharedNetwork4Test, defaults) {
EXPECT_TRUE(network->getIface().unspecified());
EXPECT_TRUE(network->getIface().empty());
- EXPECT_TRUE(network->getClientClass().unspecified());
- EXPECT_TRUE(network->getClientClass().empty());
+ EXPECT_TRUE(network->getClientClasses().empty());
EXPECT_TRUE(network->getValid().unspecified());
EXPECT_EQ(0, network->getValid().get());
@@ -829,8 +828,7 @@ TEST(SharedNetwork6Test, defaults) {
EXPECT_TRUE(network->getIface().unspecified());
EXPECT_TRUE(network->getIface().empty());
- EXPECT_TRUE(network->getClientClass().unspecified());
- EXPECT_TRUE(network->getClientClass().empty());
+ EXPECT_TRUE(network->getClientClasses().empty());
EXPECT_TRUE(network->getValid().unspecified());
EXPECT_EQ(0, network->getValid().get());
diff --git a/src/lib/dhcpsrv/tests/subnet_unittest.cc b/src/lib/dhcpsrv/tests/subnet_unittest.cc
index 1badddbd12..1a5d875f96 100644
--- a/src/lib/dhcpsrv/tests/subnet_unittest.cc
+++ b/src/lib/dhcpsrv/tests/subnet_unittest.cc
@@ -82,8 +82,7 @@ TEST(Subnet4Test, defaults) {
EXPECT_TRUE(subnet.getIface().unspecified());
EXPECT_TRUE(subnet.getIface().empty());
- EXPECT_TRUE(subnet.getClientClass().unspecified());
- EXPECT_TRUE(subnet.getClientClass().empty());
+ EXPECT_TRUE(subnet.getClientClasses().empty());
EXPECT_TRUE(subnet.getValid().unspecified());
EXPECT_EQ(0, subnet.getValid().get());
@@ -542,7 +541,7 @@ TEST(Subnet4Test, clientClass) {
four_classes.insert("network");
// No class restrictions defined, any client should be supported
- EXPECT_TRUE(subnet->getClientClass().empty());
+ EXPECT_TRUE(subnet->getClientClasses().empty());
EXPECT_TRUE(subnet->clientSupported(no_class));
EXPECT_TRUE(subnet->clientSupported(foo_class));
EXPECT_TRUE(subnet->clientSupported(bar_class));
@@ -550,7 +549,7 @@ TEST(Subnet4Test, clientClass) {
// Let's allow only clients belonging to "bar" class.
subnet->allowClientClass("bar");
- EXPECT_EQ("bar", subnet->getClientClass().get());
+ EXPECT_TRUE(subnet->getClientClasses().contains("bar"));
EXPECT_FALSE(subnet->clientSupported(no_class));
EXPECT_FALSE(subnet->clientSupported(foo_class));
@@ -880,8 +879,7 @@ TEST(SharedNetwork6Test, defaults) {
EXPECT_TRUE(subnet.getIface().unspecified());
EXPECT_TRUE(subnet.getIface().empty());
- EXPECT_TRUE(subnet.getClientClass().unspecified());
- EXPECT_TRUE(subnet.getClientClass().empty());
+ EXPECT_TRUE(subnet.getClientClasses().empty());
EXPECT_TRUE(subnet.getValid().unspecified());
EXPECT_EQ(0, subnet.getValid().get());
@@ -1261,7 +1259,7 @@ TEST(Subnet6Test, clientClass) {
four_classes.insert("network");
// No class restrictions defined, any client should be supported
- EXPECT_TRUE(subnet->getClientClass().empty());
+ EXPECT_TRUE(subnet->getClientClasses().empty());
EXPECT_TRUE(subnet->clientSupported(no_class));
EXPECT_TRUE(subnet->clientSupported(foo_class));
EXPECT_TRUE(subnet->clientSupported(bar_class));
@@ -1269,7 +1267,7 @@ TEST(Subnet6Test, clientClass) {
// Let's allow only clients belonging to "bar" class.
subnet->allowClientClass("bar");
- EXPECT_EQ("bar", subnet->getClientClass().get());
+ EXPECT_TRUE(subnet->getClientClasses().contains("bar"));
EXPECT_FALSE(subnet->clientSupported(no_class));
EXPECT_FALSE(subnet->clientSupported(foo_class));
diff --git a/src/lib/dhcpsrv/testutils/generic_cb_dhcp4_unittest.cc b/src/lib/dhcpsrv/testutils/generic_cb_dhcp4_unittest.cc
index c7452c86b3..a35a1e79f3 100644
--- a/src/lib/dhcpsrv/testutils/generic_cb_dhcp4_unittest.cc
+++ b/src/lib/dhcpsrv/testutils/generic_cb_dhcp4_unittest.cc
@@ -1145,8 +1145,7 @@ GenericConfigBackendDHCPv4Test::getSubnet4WithOptionalUnspecifiedTest() {
EXPECT_TRUE(returned_subnet->getIface().unspecified());
EXPECT_TRUE(returned_subnet->getIface().empty());
- EXPECT_TRUE(returned_subnet->getClientClass().unspecified());
- EXPECT_TRUE(returned_subnet->getClientClass().empty());
+ EXPECT_TRUE(returned_subnet->getClientClasses().empty());
EXPECT_TRUE(returned_subnet->getValid().unspecified());
EXPECT_EQ(0, returned_subnet->getValid().get());
@@ -2159,8 +2158,7 @@ GenericConfigBackendDHCPv4Test::getSharedNetwork4WithOptionalUnspecifiedTest() {
EXPECT_TRUE(returned_network->getIface().unspecified());
EXPECT_TRUE(returned_network->getIface().empty());
- EXPECT_TRUE(returned_network->getClientClass().unspecified());
- EXPECT_TRUE(returned_network->getClientClass().empty());
+ EXPECT_TRUE(returned_network->getClientClasses().empty());
EXPECT_TRUE(returned_network->getValid().unspecified());
EXPECT_EQ(0, returned_network->getValid().get());
diff --git a/src/lib/dhcpsrv/testutils/generic_cb_dhcp6_unittest.cc b/src/lib/dhcpsrv/testutils/generic_cb_dhcp6_unittest.cc
index 5130e923bc..593a51209c 100644
--- a/src/lib/dhcpsrv/testutils/generic_cb_dhcp6_unittest.cc
+++ b/src/lib/dhcpsrv/testutils/generic_cb_dhcp6_unittest.cc
@@ -1171,8 +1171,7 @@ GenericConfigBackendDHCPv6Test::getSubnet6WithOptionalUnspecifiedTest() {
EXPECT_TRUE(returned_subnet->getIface().unspecified());
EXPECT_TRUE(returned_subnet->getIface().empty());
- EXPECT_TRUE(returned_subnet->getClientClass().unspecified());
- EXPECT_TRUE(returned_subnet->getClientClass().empty());
+ EXPECT_TRUE(returned_subnet->getClientClasses().empty());
EXPECT_TRUE(returned_subnet->getValid().unspecified());
EXPECT_EQ(0, returned_subnet->getValid().get());
@@ -2177,8 +2176,7 @@ GenericConfigBackendDHCPv6Test::getSharedNetwork6WithOptionalUnspecifiedTest() {
EXPECT_TRUE(returned_network->getIface().unspecified());
EXPECT_TRUE(returned_network->getIface().empty());
- EXPECT_TRUE(returned_network->getClientClass().unspecified());
- EXPECT_TRUE(returned_network->getClientClass().empty());
+ EXPECT_TRUE(returned_network->getClientClasses().empty());
EXPECT_TRUE(returned_network->getValid().unspecified());
EXPECT_EQ(0, returned_network->getValid().get());