summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMarcin Siodelski <marcin@isc.org>2020-03-06 18:43:48 +0100
committerMarcin Siodelski <marcin@isc.org>2020-03-19 13:14:51 +0100
commit879116c5957e1e8206801afb0a48fb3215268b11 (patch)
tree2ed8db9b1218b12aa60eafa2ffc2d8e9e120daf1 /src
parent[#1139] Host based classes used in allocation (diff)
downloadkea-879116c5957e1e8206801afb0a48fb3215268b11.tar.xz
kea-879116c5957e1e8206801afb0a48fb3215268b11.zip
[#1139] Host based classes used in allocation
The change made for DHCPv6 server.
Diffstat (limited to 'src')
-rw-r--r--src/bin/dhcp6/dhcp6_srv.cc35
-rw-r--r--src/bin/dhcp6/dhcp6_srv.h8
-rw-r--r--src/bin/dhcp6/tests/host_unittest.cc163
3 files changed, 196 insertions, 10 deletions
diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc
index b3c1145b03..049f3329b8 100644
--- a/src/bin/dhcp6/dhcp6_srv.cc
+++ b/src/bin/dhcp6/dhcp6_srv.cc
@@ -424,6 +424,17 @@ Dhcpv6Srv::initContext(const Pkt6Ptr& pkt,
alloc_engine_->findReservation(ctx);
}
+ // Global host reservations are independent of a selected subnet. If the
+ // global reservations contain client classes we should use them in case
+ // they are meant to affect pool selection.
+ auto global_host = ctx.globalHost();
+ if (global_host && !global_host->getClientClasses6().empty()) {
+ // Previously evaluated classes must be ignored because having new
+ // classes fetched from the hosts db may eliminate some of them.
+ pkt->classes_.clear();
+ setReservedClientClasses(pkt, ctx);
+ }
+
// Set KNOWN builtin class if something was found, UNKNOWN if not.
if (!ctx.hosts_.empty()) {
pkt->addClass("KNOWN");
@@ -2979,7 +2990,7 @@ Dhcpv6Srv::processSolicit(AllocEngine::ClientContext6& ctx) {
processClientFqdn(solicit, response, ctx);
assignLeases(solicit, response, ctx);
- setReservedClientClasses(solicit, ctx);
+ setNonGlobalReservedClientClasses(solicit, ctx);
requiredClassify(solicit, ctx);
copyClientOptions(solicit, response);
@@ -3009,7 +3020,7 @@ Dhcpv6Srv::processRequest(AllocEngine::ClientContext6& ctx) {
processClientFqdn(request, reply, ctx);
assignLeases(request, reply, ctx);
- setReservedClientClasses(request, ctx);
+ setNonGlobalReservedClientClasses(request, ctx);
requiredClassify(request, ctx);
copyClientOptions(request, reply);
@@ -3035,7 +3046,7 @@ Dhcpv6Srv::processRenew(AllocEngine::ClientContext6& ctx) {
processClientFqdn(renew, reply, ctx);
extendLeases(renew, reply, ctx);
- setReservedClientClasses(renew, ctx);
+ setNonGlobalReservedClientClasses(renew, ctx);
requiredClassify(renew, ctx);
copyClientOptions(renew, reply);
@@ -3061,7 +3072,7 @@ Dhcpv6Srv::processRebind(AllocEngine::ClientContext6& ctx) {
processClientFqdn(rebind, reply, ctx);
extendLeases(rebind, reply, ctx);
- setReservedClientClasses(rebind, ctx);
+ setNonGlobalReservedClientClasses(rebind, ctx);
requiredClassify(rebind, ctx);
copyClientOptions(rebind, reply);
@@ -3082,7 +3093,7 @@ Pkt6Ptr
Dhcpv6Srv::processConfirm(AllocEngine::ClientContext6& ctx) {
Pkt6Ptr confirm = ctx.query_;
- setReservedClientClasses(confirm, ctx);
+ setNonGlobalReservedClientClasses(confirm, ctx);
requiredClassify(confirm, ctx);
// Get IA_NAs from the Confirm. If there are none, the message is
@@ -3172,7 +3183,7 @@ Pkt6Ptr
Dhcpv6Srv::processRelease(AllocEngine::ClientContext6& ctx) {
Pkt6Ptr release = ctx.query_;
- setReservedClientClasses(release, ctx);
+ setNonGlobalReservedClientClasses(release, ctx);
requiredClassify(release, ctx);
// Create an empty Reply message.
@@ -3198,7 +3209,7 @@ Pkt6Ptr
Dhcpv6Srv::processDecline(AllocEngine::ClientContext6& ctx) {
Pkt6Ptr decline = ctx.query_;
- setReservedClientClasses(decline, ctx);
+ setNonGlobalReservedClientClasses(decline, ctx);
requiredClassify(decline, ctx);
// Create an empty Reply message.
@@ -3493,7 +3504,7 @@ Pkt6Ptr
Dhcpv6Srv::processInfRequest(AllocEngine::ClientContext6& ctx) {
Pkt6Ptr inf_request = ctx.query_;
- setReservedClientClasses(inf_request, ctx);
+ setNonGlobalReservedClientClasses(inf_request, ctx);
requiredClassify(inf_request, ctx);
// Create a Reply packet, with the same trans-id as the client's.
@@ -3645,6 +3656,14 @@ Dhcpv6Srv::setReservedClientClasses(const Pkt6Ptr& pkt,
}
void
+Dhcpv6Srv::setNonGlobalReservedClientClasses(const Pkt6Ptr& pkt,
+ const AllocEngine::ClientContext6& ctx) {
+ if (!ctx.globalHost()) {
+ setReservedClientClasses(pkt, ctx);
+ }
+}
+
+void
Dhcpv6Srv::requiredClassify(const Pkt6Ptr& pkt, AllocEngine::ClientContext6& ctx) {
// First collect required classes
ClientClasses classes = pkt->getClasses(true);
diff --git a/src/bin/dhcp6/dhcp6_srv.h b/src/bin/dhcp6/dhcp6_srv.h
index b1716340ac..c2a4943b6c 100644
--- a/src/bin/dhcp6/dhcp6_srv.h
+++ b/src/bin/dhcp6/dhcp6_srv.h
@@ -821,6 +821,14 @@ protected:
void setReservedClientClasses(const Pkt6Ptr& pkt,
const AllocEngine::ClientContext6& ctx);
+ /// @brief Assigns non-global classes retrieved from host reservation
+ /// database.
+ ///
+ /// @param pkt Pointer to the packet to which classes will be assigned.
+ /// @param ctx Reference to the client context.
+ void setNonGlobalReservedClientClasses(const Pkt6Ptr& pkt,
+ const AllocEngine::ClientContext6& ctx);
+
/// @brief Assigns incoming packet to zero or more classes (required pass).
///
/// @note This required classification evaluates all classes which
diff --git a/src/bin/dhcp6/tests/host_unittest.cc b/src/bin/dhcp6/tests/host_unittest.cc
index 596470f4c7..d6905ab60f 100644
--- a/src/bin/dhcp6/tests/host_unittest.cc
+++ b/src/bin/dhcp6/tests/host_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2015-2018 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2015-2020 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
@@ -419,7 +419,111 @@ const char* CONFIGS[] = {
" }] \n"
" }"
" ] \n"
- "} \n"
+ "} \n",
+
+ // Configuration 10: client-class reservation in global, shared network
+ // and client-class guarded pools.
+ "{ \"interfaces-config\": {\n"
+ " \"interfaces\": [ \"*\" ]\n"
+ "},\n"
+ "\"host-reservation-identifiers\": [ \"duid\", \"hw-address\" ], \n"
+ "\"client-classes\": ["
+ "{"
+ " \"name\": \"reserved_class\""
+ "},"
+ "{"
+ " \"name\": \"unreserved_class\","
+ " \"test\": \"not member('reserved_class')\""
+ "}"
+ "],\n"
+ "\"reservation-mode\": \"global\","
+ "\"valid-lifetime\": 4000,\n"
+ "\"reservations\": [ \n"
+ "{\n"
+ " \"duid\": \"01:02:03:05\",\n"
+ " \"client-classes\": [ \"reserved_class\" ]\n"
+ "}\n"
+ "],\n"
+ "\"shared-networks\": [{"
+ " \"name\": \"frog\",\n"
+ " \"subnet6\": [\n"
+ " {\n"
+ " \"subnet\": \"2001:db8:1::/64\", \n"
+ " \"id\": 10,"
+ " \"pools\": ["
+ " {"
+ " \"pool\": \"2001:db8:1::10-2001:db8:1::11\","
+ " \"client-class\": \"reserved_class\""
+ " }"
+ " ],\n"
+ " \"interface\": \"eth0\"\n"
+ " },\n"
+ " {\n"
+ " \"subnet\": \"2001:db8:2::/64\", \n"
+ " \"id\": 11,"
+ " \"pools\": ["
+ " {"
+ " \"pool\": \"2001:db8:2::10-2001:db8:2::11\","
+ " \"client-class\": \"unreserved_class\""
+ " }"
+ " ],\n"
+ " \"interface\": \"eth0\"\n"
+ " }\n"
+ " ]\n"
+ "}]\n"
+ "}",
+
+ // Configuration 11: client-class reservation in global, shared network
+ // and client-class guarded subnets.
+ "{ \"interfaces-config\": {\n"
+ " \"interfaces\": [ \"*\" ]\n"
+ "},\n"
+ "\"host-reservation-identifiers\": [ \"duid\", \"hw-address\" ], \n"
+ "\"client-classes\": ["
+ "{"
+ " \"name\": \"reserved_class\""
+ "},"
+ "{"
+ " \"name\": \"unreserved_class\","
+ " \"test\": \"not member('reserved_class')\""
+ "}"
+ "],\n"
+ "\"reservation-mode\": \"global\","
+ "\"valid-lifetime\": 4000,\n"
+ "\"reservations\": [ \n"
+ "{\n"
+ " \"duid\": \"01:02:03:05\",\n"
+ " \"client-classes\": [ \"reserved_class\" ]\n"
+ "}\n"
+ "],\n"
+ "\"shared-networks\": [{"
+ " \"name\": \"frog\",\n"
+ " \"subnet6\": [\n"
+ " {\n"
+ " \"subnet\": \"2001:db8:1::/64\", \n"
+ " \"client-class\": \"reserved_class\","
+ " \"id\": 10,"
+ " \"pools\": ["
+ " {"
+ " \"pool\": \"2001:db8:1::10-2001:db8:1::11\""
+ " }"
+ " ],\n"
+ " \"interface\": \"eth0\"\n"
+ " },\n"
+ " {\n"
+ " \"subnet\": \"2001:db8:2::/64\", \n"
+ " \"client-class\": \"unreserved_class\","
+ " \"id\": 11,"
+ " \"pools\": ["
+ " {"
+ " \"pool\": \"2001:db8:2::10-2001:db8:2::11\""
+ " }"
+ " ],\n"
+ " \"interface\": \"eth0\"\n"
+ " }\n"
+ " ]\n"
+ "}]\n"
+ "}"
};
/// @brief Base class representing leases and hints conveyed within IAs.
@@ -813,6 +917,15 @@ public:
/// @param hint Const reference to an object holding the hint.
static void requestIA(Dhcp6Client& client, const Hint& hint);
+ /// @brief Test pool or subnet selection using global class reservation.
+ ///
+ /// Verifies that client class specified in the global reservation
+ /// may be used to influence pool or subnet selection.
+ ///
+ /// @param config_idx Index of the server configuration from the
+ /// @c CONFIGS array.
+ void testGlobalClassSubnetPoolSelection(const int config_idx);
+
/// @brief Configures client to include 6 IAs without hints.
///
/// This method configures the client to include 3 IA_NAs and
@@ -1159,6 +1272,40 @@ HostTest::testOverrideVendorOptions(const uint16_t msg_type) {
}
void
+HostTest::testGlobalClassSubnetPoolSelection(const int config_idx) {
+ Dhcp6Client client_resrv;
+
+ // Use DUID for which we have host reservation including client class.
+ client_resrv.setDUID("01:02:03:05");
+
+ ASSERT_NO_FATAL_FAILURE(configure(CONFIGS[config_idx], *client_resrv.getServer()));
+
+ // This client should be given an address from the 2001:db8:1::/64 subnet.
+ // Let's use the 2001:db8:2::10 as a hint to make sure that the server
+ // refuses allocating it and uses the sole pool available for this
+ // client.
+ client_resrv.requestAddress(1, IOAddress("2001:db8:2::10"));
+ ASSERT_NO_THROW(client_resrv.doSARR());
+ ASSERT_EQ(1, client_resrv.getLeaseNum());
+ Lease6 lease_client = client_resrv.getLease(0);
+ EXPECT_EQ("2001:db8:1::10", lease_client.addr_.toText());
+
+ // This client has no reservation and therefore should be
+ // assigned to the unreserved_class and be given an address
+ // from the other pool.
+ Dhcp6Client client_no_resrv(client_resrv.getServer());
+ client_no_resrv.setDUID("01:02:03:04");
+
+ // Let's use the address of 2001:db8:1::10 as a hint to make sure that the
+ // server refuses it in favor of the 2001:db8:2::10.
+ client_no_resrv.requestAddress(1, IOAddress("2001:db8:1::10"));
+ ASSERT_NO_THROW(client_no_resrv.doSARR());
+ ASSERT_EQ(1, client_no_resrv.getLeaseNum());
+ lease_client = client_no_resrv.getLease(0);
+ EXPECT_EQ("2001:db8:2::10", lease_client.addr_.toText());
+}
+
+void
HostTest::requestEmptyIAs(Dhcp6Client& client) {
// Create IAs with IAIDs between 1 and 6.
client.requestAddress(1);
@@ -2147,4 +2294,16 @@ TEST_F(HostTest, globalReservationsPD) {
}
}
+// Verifies that client class specified in the global reservation
+// may be used to influence pool selection.
+TEST_F(HostTest, clientClassGlobalPoolSelection) {
+ ASSERT_NO_FATAL_FAILURE(testGlobalClassSubnetPoolSelection(10));
+}
+
+// Verifies that client class specified in the global reservation
+// may be used to influence subnet selection within shared network.
+TEST_F(HostTest, clientClassGlobalSubnetSelection) {
+ ASSERT_NO_FATAL_FAILURE(testGlobalClassSubnetPoolSelection(11));
+}
+
} // end of anonymous namespace