From: Marcin Siodelski Date: Tue, 27 Feb 2018 13:38:42 +0000 (+0100) Subject: [5437] Support for HR_OUT_OF_POOL mode in DHCPv4 added. X-Git-Tag: ha_checkpoints12~10^2~7 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c0cdf5fe314aef624c1f12141b985b3cbb3d6376;p=thirdparty%2Fkea.git [5437] Support for HR_OUT_OF_POOL mode in DHCPv4 added. --- diff --git a/src/bin/dhcp4/tests/dora_unittest.cc b/src/bin/dhcp4/tests/dora_unittest.cc index 5feaf6904e..1ecd72944c 100644 --- a/src/bin/dhcp4/tests/dora_unittest.cc +++ b/src/bin/dhcp4/tests/dora_unittest.cc @@ -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(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(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(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) { diff --git a/src/lib/dhcpsrv/alloc_engine.cc b/src/lib/dhcpsrv/alloc_engine.cc index 3017adf6a5..4447eeaac5 100644 --- a/src/lib/dhcpsrv/alloc_engine.cc +++ b/src/lib/dhcpsrv/alloc_engine.cc @@ -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); }