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;
/// @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.
}
/// @}
+ /// @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
++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));
+}
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()
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";
"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",
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;
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',
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.
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
}
}
-const ClientClasses&
-Network::getAdditionalClasses() const {
- return (additional_classes_);
-}
-
Optional<IOAddress>
Network::getGlobalProperty(Optional<IOAddress> property,
const int global_index,
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
/// @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_(),
/// @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
/// 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.
/// 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
///
}
}
+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
/// @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
}
}
+ // 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,
}
}
+ // 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));
}
}
+ // 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,
}
}
+ // 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));
}
}
- 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,
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,
{ "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 },
/// 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, "" },
/// @brief This table defines default values for each IPv4 shared network.
const SimpleDefaults SimpleParser4::SHARED_NETWORK4_DEFAULTS = {
- { "client-class", Element::string, "" },
{ "interface", Element::string, "" }
};
{ "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 },
{ "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 },
{ "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 },
{ "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 },
/// 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, "" }
};
/// @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
{ "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 },
{ "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 },
{ "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 },
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
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_) {
/// @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 {
/// @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
///
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);
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);
" \"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"
" \"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"
" \"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"
" \"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"
" \"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"
" \"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"
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.
OptionPtr ifaceid = generateInterfaceId("relay.eth0");
subnet1->setInterfaceId(ifaceid);
subnet1->allowClientClass("foo");
+ subnet1->allowClientClass("bar");
subnet1->setT1Percent(0.45);
subnet1->setT2Percent(0.70);
" \"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"
" \"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"
" \"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"
" \"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"
" \"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"
" \"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"
" \"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"
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());
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());
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());
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());
" '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
// 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,
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")));
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));
// 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));
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));
// 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));
// 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());
network = parser.parse(config_element);
ASSERT_TRUE(network);
- EXPECT_EQ("alpha", network->getClientClass().get());
+ EXPECT_TRUE(network->getClientClasses().contains("alpha"));
EXPECT_FALSE(network->getMatchClientId());
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());
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
" (<string>:1:2)");
}
-
// Verify that deprecated require-client-classes is handled properly
// by v6 parser.
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);
" (<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
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());
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());
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());
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));
// 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));
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());
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));
// 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));
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());
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());
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());
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());