diff options
author | Marcin Siodelski <marcin@isc.org> | 2020-03-06 18:43:48 +0100 |
---|---|---|
committer | Marcin Siodelski <marcin@isc.org> | 2020-03-19 13:14:51 +0100 |
commit | 879116c5957e1e8206801afb0a48fb3215268b11 (patch) | |
tree | 2ed8db9b1218b12aa60eafa2ffc2d8e9e120daf1 /src | |
parent | [#1139] Host based classes used in allocation (diff) | |
download | kea-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.cc | 35 | ||||
-rw-r--r-- | src/bin/dhcp6/dhcp6_srv.h | 8 | ||||
-rw-r--r-- | src/bin/dhcp6/tests/host_unittest.cc | 163 |
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 |