]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[5437] Support for HR_OUT_OF_POOL mode in DHCPv4 added.
authorMarcin Siodelski <marcin@isc.org>
Tue, 27 Feb 2018 13:38:42 +0000 (14:38 +0100)
committerMarcin Siodelski <marcin@isc.org>
Tue, 27 Feb 2018 13:38:42 +0000 (14:38 +0100)
src/bin/dhcp4/tests/dora_unittest.cc
src/lib/dhcpsrv/alloc_engine.cc

index 5feaf6904e14b32a6f46e3c1798e5bffe5c3a2ee..1ecd72944cb339256a44820beefff1f8f3acdf93 100644 (file)
@@ -1,4 +1,4 @@
-// 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
@@ -108,6 +108,22 @@ namespace {
 ///   - Simple configuration with a single subnet
 ///   - One in-pool reservation for a circuit-id of 'charter950'
 ///
+/// - Configuration 12:
+///   - Simple configuration with a single subnet
+///   - One in-pool reservation for MAC address aa:bb:cc:dd:ee:ff
+///   - Host reservation mode set to ALL (fully enabled)
+///
+/// - Configuration 13:
+///   - Simple configuration with a single subnet as in #12
+///   - Host reservation mode set to "disabled" for testing that the
+///     reservations are ignored
+///
+/// - Configuration 14:
+///   - Simple configuration with a single subnet
+///   - Two host reservations, one out of the pool another one in pool
+///   - Host reservation mode set to "out-of-pool" to test that
+///     only out of pool reservations are honored.
+///
 const char* DORA_CONFIGS[] = {
 // Configuration 0
     "{ \"interfaces-config\": {"
@@ -444,6 +460,28 @@ const char* DORA_CONFIGS[] = {
         "       }"
         "    ]"
         "} ]"
+    "}",
+
+// Configuration 14
+    "{ \"interfaces-config\": {"
+        "      \"interfaces\": [ \"*\" ]"
+        "},"
+        "\"valid-lifetime\": 600,"
+        "\"subnet4\": [ { "
+        "    \"subnet\": \"10.0.0.0/24\","
+        "    \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ],"
+        "    \"reservation-mode\": \"out-of-pool\","
+        "    \"reservations\": [ "
+        "       {"
+        "         \"hw-address\": \"aa:bb:cc:dd:ee:ff\","
+        "         \"ip-address\": \"10.0.0.200\""
+        "       },"
+        "       {"
+        "         \"hw-address\": \"11:22:33:44:55:66\","
+        "         \"ip-address\": \"10.0.0.65\""
+        "       }"
+        "    ]"
+        "} ]"
     "}"
 };
 
@@ -1553,6 +1591,68 @@ TEST_F(DORATest, reservationModeDisabledAddressHijacking) {
     ASSERT_EQ("10.0.0.65", client.config_.lease_.addr_.toText());
 }
 
+// This test verifies that in pool reservations are ignored when the
+// reservation mode is set to "out-of-pool".
+TEST_F(DORATest, reservationModeOutOfPool) {
+    // Create the first client for which we have a reservation out of the
+    // dynamic pool.
+    Dhcp4Client clientA(Dhcp4Client::SELECTING);
+    clientA.setHWAddress("aa:bb:cc:dd:ee:ff");
+    // Configure the server to respect out of the pool reservations.
+    configure(DORA_CONFIGS[14], *clientA.getServer());
+    // The client for which we have a reservation is doing 4-way exchange
+    // and requests a different address than reserved. The server should
+    // allocate the reserved address to this client.
+    ASSERT_NO_THROW(clientA.doDORA(boost::shared_ptr<
+                                   IOAddress>(new IOAddress("10.0.0.40"))));
+    // Make sure that the server responded.
+    ASSERT_TRUE(clientA.getContext().response_);
+    Pkt4Ptr resp = clientA.getContext().response_;
+    // Make sure that the server has responded with DHCPACK.
+    ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
+    // Check that the server allocated the reserved address.
+    ASSERT_EQ("10.0.0.200", clientA.config_.lease_.addr_.toText());
+
+    // Create another client which has a reservation within the pool.
+    // The server should ignore this reservation in the current mode.
+    Dhcp4Client clientB(clientA.getServer(), Dhcp4Client::SELECTING);
+    clientB.setHWAddress("11:22:33:44:55:66");
+    // This client is requesting a different address than reserved. The
+    // server should allocate this address to the client.
+    ASSERT_NO_THROW(clientB.doDORA(boost::shared_ptr<
+                                   IOAddress>(new IOAddress("10.0.0.40"))));
+    // Make sure that the server responded.
+    ASSERT_TRUE(clientB.getContext().response_);
+    resp = clientB.getContext().response_;
+    // Make sure that the server has responded with DHCPACK.
+    ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
+    // Check that the requested address was assigned.
+    ASSERT_EQ("10.0.0.40", clientB.config_.lease_.addr_.toText());
+}
+
+// This test verifies that the in-pool reservation can be assigned to
+// the client not owning this reservation when the reservation mode is
+// set to "out-of-pool".
+TEST_F(DORATest, reservationModeOutOfPoolAddressHijacking) {
+    // Create the first client for which we have a reservation out of the
+    // dynamic pool.
+    Dhcp4Client client(Dhcp4Client::SELECTING);
+    client.setHWAddress("12:34:56:78:9A:BC");
+    // Configure the server to respect out of the pool reservations only.
+    configure(DORA_CONFIGS[14], *client.getServer());
+    // The client which doesn't have a reservation is trying to hijack
+    // the reserved address and it should succeed.
+    ASSERT_NO_THROW(client.doDORA(boost::shared_ptr<
+                                   IOAddress>(new IOAddress("10.0.0.65"))));
+    // Make sure that the server responded.
+    ASSERT_TRUE(client.getContext().response_);
+    Pkt4Ptr resp = client.getContext().response_;
+    // Make sure that the server has responded with DHCPACK.
+    ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
+    // Check that the server allocated the requested address.
+    ASSERT_EQ("10.0.0.65", client.config_.lease_.addr_.toText());
+}
+
 /// This test verifies that after a client completes its DORA exchange,
 /// appropriate statistics are updated.
 TEST_F(DORATest, statisticsDORA) {
index 3017adf6a53e744273a40eb63a0f5956d56a8ab6..4447eeaac5bffe704c11b528f1e90f2aca6a4469 100644 (file)
@@ -2512,7 +2512,10 @@ namespace {
 /// @return true if the address is reserved for another client.
 bool
 addressReserved(const IOAddress& address, const AllocEngine::ClientContext4& ctx) {
-    if (ctx.subnet_ && (ctx.subnet_->getHostReservationMode() != Network::HR_DISABLED)) {
+    if (ctx.subnet_ &&
+        ((ctx.subnet_->getHostReservationMode() == Network::HR_ALL) ||
+         ((ctx.subnet_->getHostReservationMode() == Network::HR_OUT_OF_POOL) &&
+          (!ctx.subnet_->inPool(Lease::TYPE_V4, address))))) {
         ConstHostPtr host = HostMgr::instance().get4(ctx.subnet_->getID(), address);
         if (host) {
             for (auto id = ctx.host_identifiers_.cbegin(); id != ctx.host_identifiers_.cend();
@@ -2552,7 +2555,10 @@ hasAddressReservation(AllocEngine::ClientContext4& ctx) {
     while (subnet) {
         auto host = ctx.hosts_.find(subnet->getID());
         if ((host != ctx.hosts_.end()) &&
-            !(host->second->getIPv4Reservation().isV4Zero())) {
+            !(host->second->getIPv4Reservation().isV4Zero()) &&
+            ((subnet->getHostReservationMode() == Network::HR_ALL) ||
+             ((subnet->getHostReservationMode() == Network::HR_OUT_OF_POOL) &&
+              (!subnet->inPool(Lease::TYPE_V4, host->second->getIPv4Reservation()))))) {
             ctx.subnet_ = subnet;
             return (true);
         }