/// option[93].hex == 0x0007, set server-hostname to deneb
/// option[93].hex == 0x0006, set boot-file-name to pxelinux.0
/// option[93].hex == 0x0001, set boot-file-name to ipxe.efi
+///
+/// - Configuration 4:
+/// - Used for complex membership (example taken from HA)
+/// - 1 subnet: 10.0.0.0/24
+/// - 4 pools: 10.0.0.10-10.0.0.49, 10.0.0.60-10.0.0.99,
+/// 10.0.0.110-10.0.0.149, 10.0.0.1.60-10.0.0.199
+/// - 4 classes to compose:
+/// server1 and server2 for each HA server
+/// option[93].hex == 0x0009 aka telephones
+/// option[93].hex == 0x0007 aka computers
+///
const char* CONFIGS[] = {
// Configuration 0
"{ \"interfaces-config\": {"
" \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ],"
" \"require-client-classes\": [ \"pxe2\" ]"
" } ]"
- "}"
+ "}",
+
+ // Configuration 4
+ "{ \"interfaces-config\": {"
+ " \"interfaces\": [ \"*\" ]"
+ "},"
+ "\"valid-lifetime\": 600,"
+ "\"client-classes\": ["
+ "{"
+ " \"name\": \"server1\""
+ "},"
+ "{"
+ " \"name\": \"server2\""
+ "},"
+ "{"
+ " \"name\": \"telephones\","
+ " \"test\": \"option[93].hex == 0x0009\""
+ "},"
+ "{"
+ " \"name\": \"computers\","
+ " \"test\": \"option[93].hex == 0x0007\""
+ "},"
+ "{"
+ " \"name\": \"server1_and_telephones\","
+ " \"test\": \"member('server1') and member('telephones')\""
+ "},"
+ "{"
+ " \"name\": \"server1_and_computers\","
+ " \"test\": \"member('server1') and member('computers')\""
+ "},"
+ "{"
+ " \"name\": \"server2_and_telephones\","
+ " \"test\": \"member('server2') and member('telephones')\""
+ "},"
+ "{"
+ " \"name\": \"server2_and_computers\","
+ " \"test\": \"member('server2') and member('computers')\""
+ "} ],"
+ "\"subnet4\": [ { "
+ " \"subnet\": \"10.0.0.0/24\", "
+ " \"id\": 1,"
+ " \"pools\": [ "
+ " { \"pool\": \"10.0.0.10-10.0.0.49\","
+ " \"client-class\": \"server1_and_telephones\" },"
+ " { \"pool\": \"10.0.0.60-10.0.0.99\","
+ " \"client-class\": \"server1_and_computers\" },"
+ " { \"pool\": \"10.0.0.110-10.0.0.149\","
+ " \"client-class\": \"server2_and_telephones\" },"
+ " { \"pool\": \"10.0.0.160-10.0.0.199\","
+ " \"client-class\": \"server2_and_computers\" } ]"
+ " } ]"
+ "}",
};
testFixedFields(CONFIGS[3], DHCPINFORM, pxe, "0.0.0.0", "", "ipxe.efi");
}
+// This test checks the complex membership from HA with server1 telephone.
+TEST_F(ClassifyTest, server1Telephone) {
+ Dhcp4Client client(Dhcp4Client::SELECTING);
+
+ // Configure DHCP server.
+ configure(CONFIGS[4], *client.getServer());
+
+ // Add option.
+ OptionPtr pxe(new OptionInt<uint16_t>(Option::V4, 93, 0x0009));
+ client.addExtraOption(pxe);
+
+ // Add server1
+ client.addClass("server1");
+
+ // Get an address
+ client.doDORA();
+
+ // Check response.
+ Pkt4Ptr resp = client.getContext().response_;
+ ASSERT_TRUE(resp);
+
+ // The address is from the first pool.
+ EXPECT_EQ("10.0.0.10", resp->getYiaddr().toText());
+}
+
+// This test checks the complex membership from HA with server1 computer.
+TEST_F(ClassifyTest, server1computer) {
+ Dhcp4Client client(Dhcp4Client::SELECTING);
+
+ // Configure DHCP server.
+ configure(CONFIGS[4], *client.getServer());
+
+ // Add option.
+ OptionPtr pxe(new OptionInt<uint16_t>(Option::V4, 93, 0x0007));
+ client.addExtraOption(pxe);
+
+ // Add server1
+ client.addClass("server1");
+
+ // Get an address
+ client.doDORA();
+
+ // Check response.
+ Pkt4Ptr resp = client.getContext().response_;
+ ASSERT_TRUE(resp);
+
+ // The address is from the second pool.
+ EXPECT_EQ("10.0.0.60", resp->getYiaddr().toText());
+}
+
+// This test checks the complex membership from HA with server2 telephone.
+TEST_F(ClassifyTest, server2Telephone) {
+ Dhcp4Client client(Dhcp4Client::SELECTING);
+
+ // Configure DHCP server.
+ configure(CONFIGS[4], *client.getServer());
+
+ // Add option.
+ OptionPtr pxe(new OptionInt<uint16_t>(Option::V4, 93, 0x0009));
+ client.addExtraOption(pxe);
+
+ // Add server2
+ client.addClass("server2");
+
+ // Get an address
+ client.doDORA();
+
+ // Check response.
+ Pkt4Ptr resp = client.getContext().response_;
+ ASSERT_TRUE(resp);
+
+ // The address is from the third pool.
+ EXPECT_EQ("10.0.0.110", resp->getYiaddr().toText());
+}
+
+// This test checks the complex membership from HA with server2 computer.
+TEST_F(ClassifyTest, server2computer) {
+ Dhcp4Client client(Dhcp4Client::SELECTING);
+
+ // Configure DHCP server.
+ configure(CONFIGS[4], *client.getServer());
+
+ // Add option.
+ OptionPtr pxe(new OptionInt<uint16_t>(Option::V4, 93, 0x0007));
+ client.addExtraOption(pxe);
+
+ // Add server2
+ client.addClass("server2");
+
+ // Get an address
+ client.doDORA();
+
+ // Check response.
+ Pkt4Ptr resp = client.getContext().response_;
+ ASSERT_TRUE(resp);
+
+ // The address is from the forth pool.
+ EXPECT_EQ("10.0.0.160", resp->getYiaddr().toText());
+}
+
// This test checks the precedence order in required evaluation.
// This order is: shared-network > subnet > pools
TEST_F(ClassifyTest, precedenceNone) {
-// Copyright (C) 2014-2017 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2018 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
}
}
+void
+Dhcp4Client::appendClasses() {
+ for (ClientClasses::const_iterator cclass = classes_.cbegin();
+ cclass != classes_.cend(); ++cclass) {
+ context_.query_->addClass(*cclass);
+ }
+}
+
void
Dhcp4Client::doDiscover(const boost::shared_ptr<IOAddress>& requested_addr) {
context_.query_ = createMsg(DHCPDISCOVER);
context_.query_->setCiaddr(ciaddr_.get());
}
appendExtraOptions();
+ appendClasses();
// Send the message to the server.
sendMsg(context_.query_);
appendPRL();
// Any other options to be sent by a client.
appendExtraOptions();
+ // Add classes.
+ appendClasses();
// The client sending a DHCPINFORM message has an IP address obtained
// by some other means, e.g. static configuration. The lease which we
// are using here is most likely set by the createLease method.
appendClientId();
// Any other options to be sent by a client.
appendExtraOptions();
+ // Add classes.
+ appendClasses();
// Send the message to the server.
sendMsg(context_.query_);
// Expect response.
msg_copy->setRemoteAddr(msg->getLocalAddr());
msg_copy->setLocalAddr(dest_addr_);
msg_copy->setIface(iface_name_);
+ // Copy classes
+ const ClientClasses& classes = msg->getClasses();
+ for (ClientClasses::const_iterator cclass = classes.cbegin();
+ cclass != classes.cend(); ++cclass) {
+ msg_copy->addClass(*cclass);
+ }
srv_->fakeReceive(msg_copy);
srv_->run();
}
extra_options_.insert(std::make_pair(opt->getType(), opt));
}
+void
+Dhcp4Client::addClass(const ClientClass& client_class) {
+ if (!classes_.contains(client_class)) {
+ classes_.insert(client_class);
+ }
+}
+
} // end of namespace isc::dhcp::test
} // end of namespace isc::dhcp
} // end of namespace isc
-// Copyright (C) 2014-2016 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2018 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
/// @param opt additional option to be sent
void addExtraOption(const OptionPtr& opt);
+ /// @brief Add a client class.
+ ///
+ /// @param client_class name of the class to be added.
+ void addClass(const ClientClass& client_class);
+
private:
/// @brief Appends extra options, previously added with addExtraOption()
///
/// @brief Copies options from extra_options_ into outgoing message
void appendExtraOptions();
+ /// @brief Appends extra classes, previously added with addClass()
+ ///
+ /// @brief Add client classes from classes_ to incoming message
+ void appendClasses();
+
/// @brief Creates and adds Requested IP Address option to the client's
/// query.
///
/// @brief Extra options the client will send.
OptionCollection extra_options_;
+
+ /// @brief Extra classes to add to the query.
+ ClientClasses classes_;
};
} // end of namespace isc::dhcp::test
/// the 'foo' value.
/// - There is one subnet specified 2001:db8:1::/48 with pool of
/// IPv6 addresses.
+///
+/// - Configuration 1:
+/// - Used for complex membership (example taken from HA)
+/// - 1 subnet: 2001:db8:1::/48
+/// - 4 pools: 2001:db8:1:1::/64, 2001:db8:1:2::/64,
+/// 2001:db8:1:3::/64 and 2001:db8:1:4::/64
+/// - 4 classes to compose:
+/// server1 and server2 for each HA server
+/// option 1234 'foo' aka telephones
+/// option 1234 'bar' aka computers
+///
+/// - Configuration 2:
+/// - Used for complex membership (example taken from HA) and pd-pools
+/// - 1 subnet: 2001:db8::/32
+/// - 4 pd-pools: 2001:db8:1::/48, 2001:db8:2::/48,
+/// 2001:db8:3::/48 and 2001:db8:4::/48
+/// - 4 classes to compose:
+/// server1 and server2 for each HA server
+/// option 1234 'foo' aka telephones
+/// option 1234 'bar' aka computers
+///
const char* CONFIGS[] = {
// Configuration 0
"{ \"interfaces-config\": {"
" \"client-classes\": [ \"reserved-class1\", \"reserved-class2\" ]"
" } ]"
" } ],"
+ "\"valid-lifetime\": 4000 }",
+
+ // Configuration 1
+ "{ \"interfaces-config\": {"
+ " \"interfaces\": [ \"*\" ]"
+ "},"
+ "\"preferred-lifetime\": 3000,"
+ "\"rebind-timer\": 2000, "
+ "\"renew-timer\": 1000, "
+ "\"option-def\": [ "
+ "{"
+ " \"name\": \"host-name\","
+ " \"code\": 1234,"
+ " \"type\": \"string\""
+ "} ],"
+ "\"client-classes\": ["
+ "{"
+ " \"name\": \"server1\""
+ "},"
+ "{"
+ " \"name\": \"server2\""
+ "},"
+ "{"
+ " \"name\": \"telephones\","
+ " \"test\": \"option[host-name].text == 'foo'\""
+ "},"
+ "{"
+ " \"name\": \"computers\","
+ " \"test\": \"option[host-name].text == 'bar'\""
+ "},"
+ "{"
+ " \"name\": \"server1_and_telephones\","
+ " \"test\": \"member('server1') and member('telephones')\""
+ "},"
+ "{"
+ " \"name\": \"server1_and_computers\","
+ " \"test\": \"member('server1') and member('computers')\""
+ "},"
+ "{"
+ " \"name\": \"server2_and_telephones\","
+ " \"test\": \"member('server2') and member('telephones')\""
+ "},"
+ "{"
+ " \"name\": \"server2_and_computers\","
+ " \"test\": \"member('server2') and member('computers')\""
+ "}"
+ "],"
+ "\"subnet6\": [ "
+ "{ \"subnet\": \"2001:db8:1::/48\", "
+ " \"interface\": \"eth1\","
+ " \"pools\": [ "
+ " { \"pool\": \"2001:db8:1:1::/64\","
+ " \"client-class\": \"server1_and_telephones\" },"
+ " { \"pool\": \"2001:db8:1:2::/64\","
+ " \"client-class\": \"server1_and_computers\" },"
+ " { \"pool\": \"2001:db8:1:3::/64\","
+ " \"client-class\": \"server2_and_telephones\" },"
+ " { \"pool\": \"2001:db8:1:4::/64\","
+ " \"client-class\": \"server2_and_computers\" } ]"
+ " } ],"
+ "\"valid-lifetime\": 4000 }",
+
+ // Configuration 2
+ "{ \"interfaces-config\": {"
+ " \"interfaces\": [ \"*\" ]"
+ "},"
+ "\"preferred-lifetime\": 3000,"
+ "\"rebind-timer\": 2000, "
+ "\"renew-timer\": 1000, "
+ "\"option-def\": [ "
+ "{"
+ " \"name\": \"host-name\","
+ " \"code\": 1234,"
+ " \"type\": \"string\""
+ "} ],"
+ "\"client-classes\": ["
+ "{"
+ " \"name\": \"server1\""
+ "},"
+ "{"
+ " \"name\": \"server2\""
+ "},"
+ "{"
+ " \"name\": \"telephones\","
+ " \"test\": \"option[host-name].text == 'foo'\""
+ "},"
+ "{"
+ " \"name\": \"computers\","
+ " \"test\": \"option[host-name].text == 'bar'\""
+ "},"
+ "{"
+ " \"name\": \"server1_and_telephones\","
+ " \"test\": \"member('server1') and member('telephones')\""
+ "},"
+ "{"
+ " \"name\": \"server1_and_computers\","
+ " \"test\": \"member('server1') and member('computers')\""
+ "},"
+ "{"
+ " \"name\": \"server2_and_telephones\","
+ " \"test\": \"member('server2') and member('telephones')\""
+ "},"
+ "{"
+ " \"name\": \"server2_and_computers\","
+ " \"test\": \"member('server2') and member('computers')\""
+ "}"
+ "],"
+ "\"subnet6\": [ "
+ "{ \"subnet\": \"2001:db8::/32\", "
+ " \"interface\": \"eth1\","
+ " \"pools\": [ "
+ " { \"pool\": \"2001:db8:1::/48\","
+ " \"client-class\": \"server1_and_telephones\" },"
+ " { \"pool\": \"2001:db8:2::/48\","
+ " \"client-class\": \"server1_and_computers\" },"
+ " { \"pool\": \"2001:db8:3::/48\","
+ " \"client-class\": \"server2_and_telephones\" },"
+ " { \"pool\": \"2001:db8:4::/48\","
+ " \"client-class\": \"server2_and_computers\" } ]"
+ " } ],"
"\"valid-lifetime\": 4000 }"
+
};
/// @brief Test fixture class for testing client classification by the
EXPECT_EQ("2001:db8:1::3", addrs[0].toText());
}
+// This test checks the complex membership from HA with server1 telephone.
+TEST_F(ClassifyTest, server1Telephone) {
+ // Create a client.
+ Dhcp6Client client;
+ client.setInterface("eth1");
+ ASSERT_NO_THROW(client.requestAddress(0xabca0));
+
+ // Add option.
+ OptionStringPtr hostname(new OptionString(Option::V6, 1234, "foo"));
+ client.addExtraOption(hostname);
+
+ // Add server1
+ client.addClass("server1");
+
+ // Load the config and perform a SARR
+ configure(CONFIGS[1], *client.getServer());
+ ASSERT_NO_THROW(client.doSARR());
+
+ // Check response
+ Pkt6Ptr resp = client.getContext().response_;
+ ASSERT_TRUE(resp);
+
+ // The address is from the first pool.
+ ASSERT_EQ(1, client.getLeaseNum());
+ Lease6 lease_client = client.getLease(0);
+ EXPECT_EQ("2001:db8:1:1::", lease_client.addr_.toText());
+}
+
} // end of anonymous namespace
-// Copyright (C) 2014-2017 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2018 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
}
}
+ // Add classes.
+ for (ClientClasses::const_iterator cclass = classes_.cbegin();
+ cclass != classes_.cend(); ++cclass) {
+ msg->addClass(*cclass);
+ }
+
return (msg);
}
msg_copy->setLocalAddr(dest_addr_);
msg_copy->setIface(iface_name_);
+ // Copy classes
+ const ClientClasses& classes = msg->getClasses();
+ for (ClientClasses::const_iterator cclass = classes.cbegin();
+ cclass != classes.cend(); ++cclass) {
+ msg_copy->addClass(*cclass);
+ }
+
srv_->fakeReceive(msg_copy);
srv_->run();
}
extra_options_.clear();
}
+void
+Dhcp6Client::addClass(const ClientClass& client_class) {
+ if (!classes_.contains(client_class)) {
+ classes_.insert(client_class);
+ }
+}
+
+void
+Dhcp6Client::clearClasses() {
+ classes_.clear();
+}
+
void
Dhcp6Client::printConfiguration() const {
-// Copyright (C) 2014-2017 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2018 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
/// @brief Configures the client to not send any extra options.
void clearExtraOptions();
+ /// @brief Add a client class.
+ ///
+ /// @param client_class name of the class to be added.
+ void addClass(const ClientClass& client_class);
+
+ /// @brief Configures the client to not add client classes.
+ void clearClasses();
+
/// @brief Debugging method the prints currently held configuration
///
/// @todo: This is mostly useful when debugging tests. This method
/// @brief Interface id.
OptionPtr interface_id_;
+
+ /// @brief Extra classes to add to the query.
+ ClientClasses classes_;
};
} // end of namespace isc::dhcp::test