diff --git a/src/bin/dhcp4/tests/config_parser_unittest.cc b/src/bin/dhcp4/tests/config_parser_unittest.cc index e9c2f6c232..6900615e03 100644 --- a/src/bin/dhcp4/tests/config_parser_unittest.cc +++ b/src/bin/dhcp4/tests/config_parser_unittest.cc @@ -5286,29 +5286,35 @@ TEST_F(Dhcp4ParserTest, hostReservationPerSubnet) { /// - Configuration: /// - only addresses (no prefixes) - /// - 4 subnets with: - /// - 192.0.2.0/24 (all reservations enabled) - /// - 192.0.3.0/24 (out-of-pool reservations) - /// - 192.0.4.0/24 (reservations disabled) + /// - 5 subnets with: + /// - 192.0.1.0/24 (all reservations enabled) + /// - 192.0.2.0/24 (out-of-pool reservations) + /// - 192.0.3.0/24 (reservations disabled) + /// - 192.0.4.0/24 (global reservations) /// - 192.0.5.0/24 (reservations not specified) const char* hr_config = "{ " "\"rebind-timer\": 2000, " "\"renew-timer\": 1000, " "\"subnet4\": [ { " - " \"pools\": [ { \"pool\": \"192.0.2.0/24\" } ]," - " \"subnet\": \"192.0.2.0/24\", " + " \"pools\": [ { \"pool\": \"192.0.1.0/24\" } ]," + " \"subnet\": \"192.0.1.0/24\", " " \"reservation-mode\": \"all\"" " }," " {" - " \"pools\": [ { \"pool\": \"192.0.3.0/24\" } ]," - " \"subnet\": \"192.0.3.0/24\", " + " \"pools\": [ { \"pool\": \"192.0.2.0/24\" } ]," + " \"subnet\": \"192.0.2.0/24\", " " \"reservation-mode\": \"out-of-pool\"" " }," " {" + " \"pools\": [ { \"pool\": \"192.0.3.0/24\" } ]," + " \"subnet\": \"192.0.4.0/24\", " + " \"reservation-mode\": \"disabled\"" + " }," + " {" " \"pools\": [ { \"pool\": \"192.0.4.0/24\" } ]," " \"subnet\": \"192.0.4.0/24\", " - " \"reservation-mode\": \"disabled\"" + " \"reservation-mode\": \"global\"" " }," " {" " \"pools\": [ { \"pool\": \"192.0.5.0/24\" } ]," @@ -5325,36 +5331,153 @@ TEST_F(Dhcp4ParserTest, hostReservationPerSubnet) { // returned value should be 0 (success) checkResult(result, 0); - // Let's get all subnets and check that there are 4 of them. + // Let's get all subnets and check that there are 5 of them. ConstCfgSubnets4Ptr subnets = CfgMgr::instance().getStagingCfg()->getCfgSubnets4(); ASSERT_TRUE(subnets); const Subnet4Collection* subnet_col = subnets->getAll(); - ASSERT_EQ(4, subnet_col->size()); // We expect 4 subnets + ASSERT_EQ(5, subnet_col->size()); // We expect 5 subnets // Let's check if the parsed subnets have correct HR modes. // Subnet 1 Subnet4Ptr subnet; - subnet = subnets->selectSubnet(IOAddress("192.0.2.1")); + subnet = subnets->selectSubnet(IOAddress("192.0.1.1")); ASSERT_TRUE(subnet); EXPECT_EQ(Network::HR_ALL, subnet->getHostReservationMode()); // Subnet 2 - subnet = subnets->selectSubnet(IOAddress("192.0.3.1")); + subnet = subnets->selectSubnet(IOAddress("192.0.2.1")); ASSERT_TRUE(subnet); EXPECT_EQ(Network::HR_OUT_OF_POOL, subnet->getHostReservationMode()); // Subnet 3 - subnet = subnets->selectSubnet(IOAddress("192.0.4.1")); + subnet = subnets->selectSubnet(IOAddress("192.0.3.1")); ASSERT_TRUE(subnet); EXPECT_EQ(Network::HR_DISABLED, subnet->getHostReservationMode()); // Subnet 4 + subnet = subnets->selectSubnet(IOAddress("192.0.4.1")); + ASSERT_TRUE(subnet); + EXPECT_EQ(Network::HR_GLOBAL, subnet->getHostReservationMode()); + + // Subnet 5 subnet = subnets->selectSubnet(IOAddress("192.0.5.1")); ASSERT_TRUE(subnet); EXPECT_EQ(Network::HR_ALL, subnet->getHostReservationMode()); } +/// The goal of this test is to verify that Host Reservation modes can be +/// specified on a per-subnet basis. +TEST_F(Dhcp4ParserTest, hostReservationModesPerSubnet) { + + /// - Configuration: + /// - only addresses (no prefixes) + /// - 6 subnets with: + /// - 192.0.1.0/24 (all reservations enabled) + /// - 192.0.2.0/24 (out-of-pool reservations) + /// - 192.0.3.0/24 (reservations disabled) + /// - 192.0.4.0/24 (global reservations) + /// - 192.0.5.0/24 (reservations not specified) + /// - 192.0.6.0/24 (global + all enabled) + const char* hr_config = + "{ " + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"subnet4\": [ { " + " \"pools\": [ { \"pool\": \"192.0.1.0/24\" } ]," + " \"subnet\": \"192.0.1.0/24\", " + " \"reservation-modes\": {" + " \"in-subnet\": True," + " \"out-of-pool\": True" + " }" + " }," + " {" + " \"pools\": [ { \"pool\": \"192.0.2.0/24\" } ]," + " \"subnet\": \"192.0.2.0/24\", " + " \"reservation-modes\": {" + " \"out-of-pool\": True" + " }" + " }," + " {" + " \"pools\": [ { \"pool\": \"192.0.3.0/24\" } ]," + " \"subnet\": \"192.0.4.0/24\", " + " \"reservation-modes\": {" + " \"in-subnet\": False," + " \"out-of-pool\": False," + " \"global\": False" + " }" + " }," + " {" + " \"pools\": [ { \"pool\": \"192.0.4.0/24\" } ]," + " \"subnet\": \"192.0.4.0/24\", " + " \"reservation-modes\": {" + " \"global\": True" + " }" + " }," + " {" + " \"pools\": [ { \"pool\": \"192.0.5.0/24\" } ]," + " \"subnet\": \"192.0.5.0/24\"" + " }," + " {" + " \"pools\": [ { \"pool\": \"192.0.6.0/24\" } ]," + " \"subnet\": \"192.0.6.0/24\", " + " \"reservation-modes\": {" + " \"in-subnet\": True," + " \"out-of-pool\": True," + " \"global\": True" + " }" + " } ]," + "\"valid-lifetime\": 4000 }"; + + ConstElementPtr json; + ASSERT_NO_THROW(json = parseDHCP4(hr_config)); + extractConfig(hr_config); + ConstElementPtr result; + EXPECT_NO_THROW(result = configureDhcp4Server(*srv_, json)); + + // returned value should be 0 (success) + checkResult(result, 0); + + // Let's get all subnets and check that there are 6 of them. + ConstCfgSubnets4Ptr subnets = CfgMgr::instance().getStagingCfg()->getCfgSubnets4(); + ASSERT_TRUE(subnets); + const Subnet4Collection* subnet_col = subnets->getAll(); + ASSERT_EQ(6, subnet_col->size()); // We expect 6 subnets + + // Let's check if the parsed subnets have correct HR modes. + + // Subnet 1 + Subnet4Ptr subnet; + subnet = subnets->selectSubnet(IOAddress("192.0.1.1")); + ASSERT_TRUE(subnet); + EXPECT_EQ(Network::HR_ALL, subnet->getHostReservationMode()); + + // Subnet 2 + subnet = subnets->selectSubnet(IOAddress("192.0.2.1")); + ASSERT_TRUE(subnet); + EXPECT_EQ(Network::HR_OUT_OF_POOL, subnet->getHostReservationMode()); + + // Subnet 3 + subnet = subnets->selectSubnet(IOAddress("192.0.3.1")); + ASSERT_TRUE(subnet); + EXPECT_EQ(Network::HR_DISABLED, subnet->getHostReservationMode()); + + // Subnet 4 + subnet = subnets->selectSubnet(IOAddress("192.0.4.1")); + ASSERT_TRUE(subnet); + EXPECT_EQ(Network::HR_GLOBAL, subnet->getHostReservationMode()); + + // Subnet 5 + subnet = subnets->selectSubnet(IOAddress("192.0.5.1")); + ASSERT_TRUE(subnet); + EXPECT_EQ(Network::HR_ALL, subnet->getHostReservationMode()); + + // Subnet 6 + subnet = subnets->selectSubnet(IOAddress("192.0.6.1")); + ASSERT_TRUE(subnet); + EXPECT_EQ(Network::HR_ALL|Network::HR_GLOBAL, subnet->getHostReservationMode()); +} + /// The goal of this test is to verify that Host Reservation modes can be /// specified globally. TEST_F(Dhcp4ParserTest, hostReservationGlobal) { @@ -5417,6 +5540,73 @@ TEST_F(Dhcp4ParserTest, hostReservationGlobal) { EXPECT_EQ(Network::HR_OUT_OF_POOL, subnet->getHostReservationMode()); } +/// The goal of this test is to verify that Host Reservation modes can be +/// specified globally. +TEST_F(Dhcp4ParserTest, hostReservationModesGlobal) { + + /// - Configuration: + /// - only addresses (no prefixes) + /// - 2 subnets with : + /// - 192.0.2.0/24 (all reservations enabled) + /// - 192.0.3.0/24 (reservations not specified) + const char* hr_config = + "{ " + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"reservation-modes\": {" + " \"out-of-pool\": True" + " }," + "\"subnet4\": [ { " + " \"pools\": [ { \"pool\": \"192.0.2.0/24\" } ]," + " \"subnet\": \"192.0.2.0/24\", " + " \"reservation-modes\": {" + " \"in-subnet\": True," + " \"out-of-pool\": True" + " }" + " }," + " {" + " \"pools\": [ { \"pool\": \"192.0.3.0/24\" } ]," + " \"subnet\": \"192.0.3.0/24\"" + " } ]," + "\"valid-lifetime\": 4000 }"; + + ConstElementPtr json; + ASSERT_NO_THROW(json = parseDHCP4(hr_config)); + extractConfig(hr_config); + ConstElementPtr result; + EXPECT_NO_THROW(result = configureDhcp4Server(*srv_, json)); + + // returned value should be 0 (success) + checkResult(result, 0); + + // Let's get all subnets and check that there are 4 of them. + ConstCfgSubnets4Ptr subnets = CfgMgr::instance().getStagingCfg()->getCfgSubnets4(); + ASSERT_TRUE(subnets); + const Subnet4Collection* subnet_col = subnets->getAll(); + ASSERT_EQ(2, subnet_col->size()); // We expect 2 subnets + + // Let's check if the parsed subnets have correct HR modes. + + // Subnet 1 + Subnet4Ptr subnet; + subnet = subnets->selectSubnet(IOAddress("192.0.2.1")); + ASSERT_TRUE(subnet); + // Reset the fetch global function to staging (vs current) config. + subnet->setFetchGlobalsFn([]() -> ConstElementPtr { + return (CfgMgr::instance().getStagingCfg()->getConfiguredGlobals()); + }); + EXPECT_EQ(Network::HR_ALL, subnet->getHostReservationMode()); + + // Subnet 2 + subnet = subnets->selectSubnet(IOAddress("192.0.3.1")); + ASSERT_TRUE(subnet); + // Reset the fetch global function to staging (vs current) config. + subnet->setFetchGlobalsFn([]() -> ConstElementPtr { + return (CfgMgr::instance().getStagingCfg()->getConfiguredGlobals()); + }); + EXPECT_EQ(Network::HR_OUT_OF_POOL, subnet->getHostReservationMode()); +} + /// Check that the decline-probation-period has a default value when not /// specified. TEST_F(Dhcp4ParserTest, declineTimerDefault) { @@ -6271,7 +6461,9 @@ TEST_F(Dhcp4ParserTest, sharedNetworksDerive) { " \"relay\": {\n" " \"ip-address\": \"5.6.7.8\"\n" " },\n" - " \"reservation-mode\": \"out-of-pool\",\n" + " \"reservation-modes\": {" + " \"out-of-pool\": True" + " }," " \"renew-timer\": 10,\n" " \"rebind-timer\": 20,\n" " \"valid-lifetime\": 40,\n" @@ -6297,7 +6489,9 @@ TEST_F(Dhcp4ParserTest, sharedNetworksDerive) { " \"relay\": {\n" " \"ip-address\": \"55.66.77.88\"\n" " },\n" - " \"reservation-mode\": \"disabled\"\n" + " \"reservation-modes\": {" + " \"global\": False" + " }" " }\n" " ]\n" " },\n" diff --git a/src/bin/dhcp6/tests/config_parser_unittest.cc b/src/bin/dhcp6/tests/config_parser_unittest.cc index f68c2e3251..61755958ec 100644 --- a/src/bin/dhcp6/tests/config_parser_unittest.cc +++ b/src/bin/dhcp6/tests/config_parser_unittest.cc @@ -5738,6 +5738,121 @@ TEST_F(Dhcp6ParserTest, hostReservationPerSubnet) { EXPECT_EQ(Network::HR_ALL, subnet->getHostReservationMode()); } +/// The goal of this test is to verify that Host Reservation modes can be +/// specified on a per-subnet basis. +TEST_F(Dhcp6ParserTest, hostReservationModesPerSubnet) { + + /// - Configuration: + /// - only addresses (no prefixes) + /// - 6 subnets with: + /// - 2001:db8:1::/64 (all reservations enabled) + /// - 2001:db8:2::/64 (out-of-pool reservations) + /// - 2001:db8:3::/64 (reservations disabled) + /// - 2001:db8:4::/64 (global reservations) + /// - 2001:db8:5::/64 (reservations not specified) + /// - 2001:db8:5::/64 (global + all enabled) + const char* HR_CONFIG = + "{" + "\"preferred-lifetime\": 3000," + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"subnet6\": [ { " + " \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ]," + " \"subnet\": \"2001:db8:1::/48\", " + " \"reservation-modes\": {" + " \"in-subnet\": True," + " \"out-of-pool\": True" + " }" + " }," + " {" + " \"pools\": [ { \"pool\": \"2001:db8:2::/64\" } ]," + " \"subnet\": \"2001:db8:2::/48\", " + " \"reservation-modes\": {" + " \"out-of-pool\": True" + " }" + " }," + " {" + " \"pools\": [ { \"pool\": \"2001:db8:3::/64\" } ]," + " \"subnet\": \"2001:db8:3::/48\", " + " \"reservation-modes\": {" + " \"in-subnet\": False," + " \"out-of-pool\": False," + " \"global\": False" + " }" + " }," + " {" + " \"pools\": [ { \"pool\": \"2001:db8:4::/64\" } ]," + " \"subnet\": \"2001:db8:4::/48\", " + " \"reservation-modes\": {" + " \"global\": True" + " }" + " }," + " {" + " \"pools\": [ { \"pool\": \"2001:db8:5::/64\" } ]," + " \"subnet\": \"2001:db8:5::/48\" " + " }," + " {" + " \"pools\": [ { \"pool\": \"2001:db8:6::/64\" } ]," + " \"subnet\": \"2001:db8:6::/48\", " + " \"reservation-modes\": {" + " \"in-subnet\": True," + " \"out-of-pool\": True," + " \"global\": True" + " }" + " } ]," + "\"valid-lifetime\": 4000 }"; + + ConstElementPtr json; + ASSERT_NO_THROW(json = parseDHCP6(HR_CONFIG)); + extractConfig(HR_CONFIG); + + ConstElementPtr status; + EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json)); + + // returned value should be 0 (success) + checkResult(status, 0); + CfgMgr::instance().commit(); + + // Let's get all subnets and check that there are 6 of them. + ConstCfgSubnets6Ptr subnets = CfgMgr::instance().getCurrentCfg()->getCfgSubnets6(); + ASSERT_TRUE(subnets); + const Subnet6Collection* subnet_col = subnets->getAll(); + ASSERT_EQ(6, subnet_col->size()); // We expect 6 subnets + + // Let's check if the parsed subnets have correct HR modes. + + // Subnet 1 + Subnet6Ptr subnet; + subnet = subnets->selectSubnet(IOAddress("2001:db8:1::1")); + ASSERT_TRUE(subnet); + EXPECT_EQ(Network::HR_ALL, subnet->getHostReservationMode()); + + // Subnet 2 + subnet = subnets->selectSubnet(IOAddress("2001:db8:2::1")); + ASSERT_TRUE(subnet); + EXPECT_EQ(Network::HR_OUT_OF_POOL, subnet->getHostReservationMode()); + + // Subnet 3 + subnet = subnets->selectSubnet(IOAddress("2001:db8:3::1")); + ASSERT_TRUE(subnet); + EXPECT_EQ(Network::HR_DISABLED, subnet->getHostReservationMode()); + + // Subnet 4 + subnet = subnets->selectSubnet(IOAddress("2001:db8:4::1")); + ASSERT_TRUE(subnet); + EXPECT_EQ(Network::HR_GLOBAL, subnet->getHostReservationMode()); + + // Subnet 5 + subnet = subnets->selectSubnet(IOAddress("2001:db8:5::1")); + ASSERT_TRUE(subnet); + EXPECT_EQ(Network::HR_ALL, subnet->getHostReservationMode()); + + // Subnet 6 + subnet = subnets->selectSubnet(IOAddress("2001:db8:6::1")); + ASSERT_TRUE(subnet); + EXPECT_EQ(Network::HR_ALL|Network::HR_GLOBAL, subnet->getHostReservationMode()); +} + /// The goal of this test is to verify that Host Reservation modes can be /// specified globally. TEST_F(Dhcp6ParserTest, hostReservationGlobal) { @@ -5795,6 +5910,68 @@ TEST_F(Dhcp6ParserTest, hostReservationGlobal) { EXPECT_EQ(Network::HR_OUT_OF_POOL, subnet->getHostReservationMode()); } +/// The goal of this test is to verify that Host Reservation modes can be +/// specified globally. +TEST_F(Dhcp6ParserTest, hostReservationModesGlobal) { + + /// - Configuration: + /// - only addresses (no prefixes) + /// - 2 subnets with: + /// - 2001:db8:1::/64 (all reservations enabled) + /// - 2001:db8:2::/64 (reservations not specified) + const char* HR_CONFIG = + "{" + "\"preferred-lifetime\": 3000," + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"reservation-modes\": {" + " \"out-of-pool\": True" + " }," + "\"subnet6\": [ { " + " \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ]," + " \"subnet\": \"2001:db8:1::/48\", " + " \"reservation-modes\": {" + " \"in-subnet\": True," + " \"out-of-pool\": True" + " }" + " }," + " {" + " \"pools\": [ { \"pool\": \"2001:db8:2::/64\" } ]," + " \"subnet\": \"2001:db8:2::/48\" " + " } ]," + "\"valid-lifetime\": 4000 }"; + + ConstElementPtr json; + ASSERT_NO_THROW(json = parseDHCP6(HR_CONFIG)); + extractConfig(HR_CONFIG); + + ConstElementPtr status; + EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json)); + + // returned value should be 0 (success) + checkResult(status, 0); + CfgMgr::instance().commit(); + + // Let's get all subnets and check that there are 2 of them. + ConstCfgSubnets6Ptr subnets = CfgMgr::instance().getCurrentCfg()->getCfgSubnets6(); + ASSERT_TRUE(subnets); + const Subnet6Collection* subnet_col = subnets->getAll(); + ASSERT_EQ(2, subnet_col->size()); // We expect 2 subnets + + // Let's check if the parsed subnets have correct HR modes. + + // Subnet 1 + Subnet6Ptr subnet; + subnet = subnets->selectSubnet(IOAddress("2001:db8:1::1")); + ASSERT_TRUE(subnet); + EXPECT_EQ(Network::HR_ALL, subnet->getHostReservationMode()); + + // Subnet 2 + subnet = subnets->selectSubnet(IOAddress("2001:db8:2::1")); + ASSERT_TRUE(subnet); + EXPECT_EQ(Network::HR_OUT_OF_POOL, subnet->getHostReservationMode()); +} + /// The goal of this test is to verify that configuration can include /// Relay Supplied options (specified as numbers). TEST_F(Dhcp6ParserTest, rsooNumbers) { @@ -6714,7 +6891,9 @@ TEST_F(Dhcp6ParserTest, sharedNetworksDerive) { " \"ip-address\": \"1111::1\"\n" " },\n" " \"rapid-commit\": true,\n" - " \"reservation-mode\": \"disabled\",\n" + " \"reservation-modes\": {" + " \"global\": False" + " }" " \"subnet6\": [\n" " { \n" " \"subnet\": \"2001:db1::/48\",\n" @@ -6736,7 +6915,9 @@ TEST_F(Dhcp6ParserTest, sharedNetworksDerive) { " \"max-valid-lifetime\": 500, \n" " \"interface-id\": \"twotwo\",\n" " \"rapid-commit\": true,\n" - " \"reservation-mode\": \"out-of-pool\"\n" + " \"reservation-modes\": {" + " \"out-of-pool\": True" + " }" " }\n" " ]\n" " },\n" diff --git a/src/lib/dhcpsrv/alloc_engine.cc b/src/lib/dhcpsrv/alloc_engine.cc index 6ea19bb024..17411ff4f9 100644 --- a/src/lib/dhcpsrv/alloc_engine.cc +++ b/src/lib/dhcpsrv/alloc_engine.cc @@ -536,7 +536,7 @@ ConstHostPtr AllocEngine::ClientContext6::currentHost() const { Subnet6Ptr subnet = host_subnet_ ? host_subnet_ : subnet_; if (subnet) { - SubnetID id = (subnet_->getHostReservationMode() == Network::HR_GLOBAL ? + SubnetID id = (subnet_->getHostReservationMode() & Network::HR_GLOBAL ? SUBNET_ID_GLOBAL : subnet->getID()); auto host = hosts_.find(id); @@ -551,7 +551,7 @@ AllocEngine::ClientContext6::currentHost() const { ConstHostPtr AllocEngine::ClientContext6::globalHost() const { Subnet6Ptr subnet = host_subnet_ ? host_subnet_ : subnet_; - if (subnet && subnet_->getHostReservationMode() == Network::HR_GLOBAL) { + if (subnet && (subnet_->getHostReservationMode() & Network::HR_GLOBAL)) { auto host = hosts_.find(SUBNET_ID_GLOBAL); if (host != hosts_.cend()) { return (host->second); @@ -599,14 +599,16 @@ void AllocEngine::findReservation(ClientContext6& ctx) { SharedNetwork6Ptr network; subnet->getSharedNetwork(network); - if (subnet->getHostReservationMode() == Network::HR_GLOBAL) { + if (subnet->getHostReservationMode() & Network::HR_GLOBAL) { ConstHostPtr ghost = findGlobalReservation(ctx); if (ghost) { ctx.hosts_[SUBNET_ID_GLOBAL] = ghost; // @todo In theory, to support global as part of HR_ALL, // we would just keep going, instead of returning. - return; + if (subnet->getHostReservationMode() == Network::HR_GLOBAL) { + return; + } } } @@ -1072,7 +1074,7 @@ AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) { } // First check for reservation when it is the choice. - if (check_reservation_first && (hr_mode == Network::HR_ALL)) { + if (check_reservation_first && (hr_mode & Network::HR_IN_SUBNET)) { auto hosts = getIPv6Resrv(subnet->getID(), candidate); if (!hosts.empty()) { // Don't allocate. @@ -1097,7 +1099,7 @@ AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) { /// In-pool reservations: Check if this address is reserved for someone /// else. There is no need to check for whom it is reserved, because if /// it has been reserved for us we would have already allocated a lease. - if (!check_reservation_first && (hr_mode == Network::HR_ALL)) { + if (!check_reservation_first && (hr_mode & Network::HR_IN_SUBNET)) { auto hosts = getIPv6Resrv(subnet->getID(), candidate); if (!hosts.empty()) { // Don't allocate. @@ -1129,7 +1131,7 @@ AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) { // allocation attempts. } else if (existing->expired()) { // Make sure it's not reserved. - if (!check_reservation_first && (hr_mode == Network::HR_ALL)) { + if (!check_reservation_first && (hr_mode & Network::HR_IN_SUBNET)) { auto hosts = getIPv6Resrv(subnet->getID(), candidate); if (!hosts.empty()) { // Don't allocate. @@ -3211,7 +3213,7 @@ AllocEngine::ClientContext4::ClientContext4(const Subnet4Ptr& subnet, ConstHostPtr AllocEngine::ClientContext4::currentHost() const { if (subnet_) { - SubnetID id = (subnet_->getHostReservationMode() == Network::HR_GLOBAL ? + SubnetID id = (subnet_->getHostReservationMode() & Network::HR_GLOBAL ? SUBNET_ID_GLOBAL : subnet_->getID()); auto host = hosts_.find(id); @@ -3224,7 +3226,7 @@ AllocEngine::ClientContext4::currentHost() const { ConstHostPtr AllocEngine::ClientContext4::globalHost() const { - if (subnet_ && subnet_->getHostReservationMode() == Network::HR_GLOBAL) { + if (subnet_ && (subnet_->getHostReservationMode() & Network::HR_GLOBAL)) { auto host = hosts_.find(SUBNET_ID_GLOBAL); if (host != hosts_.cend()) { return (host->second); @@ -3311,7 +3313,7 @@ AllocEngine::findReservation(ClientContext4& ctx) { SharedNetwork4Ptr network; subnet->getSharedNetwork(network); - if (subnet->getHostReservationMode() == Network::HR_GLOBAL) { + if (subnet->getHostReservationMode() & Network::HR_GLOBAL) { ConstHostPtr ghost = findGlobalReservation(ctx); if (ghost) { ctx.hosts_[SUBNET_ID_GLOBAL] = ghost; diff --git a/src/lib/dhcpsrv/network.h b/src/lib/dhcpsrv/network.h index 95cb0465a1..75046175c4 100644 --- a/src/lib/dhcpsrv/network.h +++ b/src/lib/dhcpsrv/network.h @@ -451,19 +451,25 @@ public: util::Optional hr_mode_global; getGlobalProperty(hr_mode_global, "reservation-modes.global"); if (!hr_mode_global.unspecified()) { - flags |= Network::HR_GLOBAL; + if (hr_mode_global.get()) { + flags |= Network::HR_GLOBAL; + } found = true; } util::Optional hr_mode_in_subnet; getGlobalProperty(hr_mode_in_subnet, "reservation-modes.in-subnet"); if (!hr_mode_in_subnet.unspecified()) { - flags |= Network::HR_IN_SUBNET; + if (hr_mode_in_subnet.get()) { + flags |= Network::HR_IN_SUBNET; + } found = true; } util::Optional hr_mode_out_of_pool; getGlobalProperty(hr_mode_out_of_pool, "reservation-modes.out-of-pool"); if (!hr_mode_out_of_pool.unspecified()) { - flags |= Network::HR_OUT_OF_POOL; + if (hr_mode_out_of_pool.get()) { + flags |= Network::HR_OUT_OF_POOL; + } found = true; } if (found) { diff --git a/src/lib/dhcpsrv/shared_network.cc b/src/lib/dhcpsrv/shared_network.cc index cc495b4154..3d80c3c656 100644 --- a/src/lib/dhcpsrv/shared_network.cc +++ b/src/lib/dhcpsrv/shared_network.cc @@ -399,16 +399,6 @@ SharedNetwork4::subnetsIncludeMatchClientId(const Subnet4Ptr& first_subnet, return (false); } -Subnet4Ptr -SharedNetwork4::subnetsAllHRGlobal() const { - for (auto subnet : *getAllSubnets()) { - if (subnet->getHostReservationMode() != Network::HR_GLOBAL) { - return (subnet); - } - } - return (Subnet4Ptr()); -} - ElementPtr SharedNetwork4::toElement() const { ElementPtr map = Network4::toElement(); @@ -498,16 +488,6 @@ SharedNetwork6::getPreferredSubnet(const Subnet6Ptr& selected_subnet, return (Impl::getPreferredSubnet(subnets_, selected_subnet, lease_type)); } -Subnet6Ptr -SharedNetwork6::subnetsAllHRGlobal() const { - for (auto subnet : *getAllSubnets()) { - if (subnet->getHostReservationMode() != Network::HR_GLOBAL) { - return (subnet); - } - } - return (Subnet6Ptr()); -} - ElementPtr SharedNetwork6::toElement() const { ElementPtr map = Network6::toElement(); diff --git a/src/lib/dhcpsrv/shared_network.h b/src/lib/dhcpsrv/shared_network.h index e463033ed4..9dd939cbd8 100644 --- a/src/lib/dhcpsrv/shared_network.h +++ b/src/lib/dhcpsrv/shared_network.h @@ -195,12 +195,6 @@ public: bool subnetsIncludeMatchClientId(const Subnet4Ptr& first_subnet, const ClientClasses& client_classes); - /// @brief Check if the shared network includes a subnet with - /// not global host reservation mode. - /// - /// @return First subnet which has not a global host reservation mode. - Subnet4Ptr subnetsAllHRGlobal() const; - /// @brief Unparses shared network object. /// /// @return A pointer to unparsed shared network configuration. @@ -399,12 +393,6 @@ public: Subnet6Ptr getPreferredSubnet(const Subnet6Ptr& selected_subnet, const Lease::Type& lease_type) const; - /// @brief Check if the shared network includes a subnet with - /// not global host reservation mode. - /// - /// @return First subnet which has not a global host reservation mode. - Subnet6Ptr subnetsAllHRGlobal() const; - /// @brief Unparses shared network object. /// /// @return A pointer to unparsed shared network configuration. diff --git a/src/lib/dhcpsrv/tests/network_unittest.cc b/src/lib/dhcpsrv/tests/network_unittest.cc index a04ac30485..c94f30f3d7 100644 --- a/src/lib/dhcpsrv/tests/network_unittest.cc +++ b/src/lib/dhcpsrv/tests/network_unittest.cc @@ -185,6 +185,11 @@ TEST_F(NetworkTest, inheritanceSupport4) { globals_->set("cache-max-age", Element::create(20)); globals_->set("ddns-update-on-renew", Element::create(true)); globals_->set("ddns-use-conflict-resolution", Element::create(true)); + auto reservation_modes = Element::createMap(); + reservation_modes->set("global", Element::create(false)); + reservation_modes->set("in-subnet", Element::create(false)); + reservation_modes->set("out-of-pool", Element::create(false)); + globals_->set("reservation-modes", reservation_modes); // For each parameter for which inheritance is supported run // the test that checks if the values are inherited properly. @@ -217,6 +222,13 @@ TEST_F(NetworkTest, inheritanceSupport4) { Network::HR_OUT_OF_POOL, Network::HR_DISABLED); } + { + SCOPED_TRACE("reservation-modes"); + testNetworkInheritance(&Network::getHostReservationMode, + &Network::setHostReservationMode, + Network::HR_OUT_OF_POOL, + Network::HR_DISABLED); + } { SCOPED_TRACE("calculate-tee-times"); testNetworkInheritance(&Network::getCalculateTeeTimes, diff --git a/src/lib/dhcpsrv/tests/shared_network_parser_unittest.cc b/src/lib/dhcpsrv/tests/shared_network_parser_unittest.cc index dc17c8a626..f3d0b8f13d 100644 --- a/src/lib/dhcpsrv/tests/shared_network_parser_unittest.cc +++ b/src/lib/dhcpsrv/tests/shared_network_parser_unittest.cc @@ -128,7 +128,7 @@ public: " \"rebind-timer\": 199," " \"relay\": { \"ip-addresses\": [ \"10.1.1.1\" ] }," " \"renew-timer\": 99," - " \"reservation-mode\": \"out-of-pool\"," + " \"reservation-modes\": {\"out-of-pool\": True}," " \"server-hostname\": \"example.org\"," " \"require-client-classes\": [ \"runner\" ]," " \"user-context\": { \"comment\": \"example\" }," @@ -549,7 +549,7 @@ public: " \"relay\": { \"ip-addresses\": [ \"2001:db8:1::1\" ] }," " \"renew-timer\": 99," " \"require-client-classes\": [ \"runner\" ]," - " \"reservation-mode\": \"out-of-pool\"," + " \"reservation-modes\": {\"out-of-pool\": True}," " \"user-context\": { }," " \"valid-lifetime\": 399," " \"min-valid-lifetime\": 299," diff --git a/src/lib/dhcpsrv/tests/shared_network_unittest.cc b/src/lib/dhcpsrv/tests/shared_network_unittest.cc index 03864b8c73..a1f6c30e4c 100644 --- a/src/lib/dhcpsrv/tests/shared_network_unittest.cc +++ b/src/lib/dhcpsrv/tests/shared_network_unittest.cc @@ -576,36 +576,6 @@ TEST(SharedNetwork4Test, subnetsIncludeMatchClientId) { EXPECT_TRUE(SharedNetwork4::subnetsIncludeMatchClientId(subnet1, classes)); } -// This test verifies that subnetsAllHRGlobal() works as expected. -TEST(SharedNetwork4Test, subnetsAllHRGlobal) { - SharedNetwork4Ptr network(new SharedNetwork4("frog")); - Subnet4Ptr bad; - - // Empty shared network is right. - ASSERT_NO_THROW(bad = network->subnetsAllHRGlobal()); - EXPECT_FALSE(bad); - - // Create a subnet and add it to the shared network. - Subnet4Ptr subnet(new Subnet4(IOAddress("10.0.0.0"), 8, 10, 20, 30, - SubnetID(1))); - ASSERT_NO_THROW(network->add(subnet)); - - // Default host reservation mode is ALL. - bad.reset(); - ASSERT_NO_THROW(bad = network->subnetsAllHRGlobal()); - ASSERT_TRUE(bad); - EXPECT_EQ(1, bad->getID()); - EXPECT_EQ("10.0.0.0/8", bad->toText()); - - // Set the HR mode to global. - subnet->setHostReservationMode(Network::HR_GLOBAL); - - // Now the shared network is all global. - bad.reset(); - ASSERT_NO_THROW(bad = network->subnetsAllHRGlobal()); - EXPECT_FALSE(bad); -} - // This test verifies operations on the network's relay list TEST(SharedNetwork4Test, relayInfoList) { SharedNetwork4Ptr network(new SharedNetwork4("frog")); @@ -1298,36 +1268,6 @@ TEST(SharedNetwork6Test, getPreferredSubnetMultiThreading) { EXPECT_EQ(subnet3->getID(), preferred->getID()); } -// This test verifies that subnetsAllHRGlobal() works as expected. -TEST(SharedNetwork6Test, subnetsAllHRGlobal) { - SharedNetwork6Ptr network(new SharedNetwork6("frog")); - Subnet6Ptr bad; - - // Empty shared network is right. - ASSERT_NO_THROW(bad = network->subnetsAllHRGlobal()); - EXPECT_FALSE(bad); - - // Create a subnet and add it to the shared network. - Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8:1::"), 64, 10, 20, 30, - 40, SubnetID(1))); - ASSERT_NO_THROW(network->add(subnet)); - - // Default host reservation mode is ALL. - bad.reset(); - ASSERT_NO_THROW(bad = network->subnetsAllHRGlobal()); - ASSERT_TRUE(bad); - EXPECT_EQ(1, bad->getID()); - EXPECT_EQ("2001:db8:1::/64", bad->toText()); - - // Set the HR mode to global. - subnet->setHostReservationMode(Network::HR_GLOBAL); - - // Now the shared network is all global. - bad.reset(); - ASSERT_NO_THROW(bad = network->subnetsAllHRGlobal()); - EXPECT_FALSE(bad); -} - // This test verifies operations on the network's relay list TEST(SharedNetwork6Test, relayInfoList) { SharedNetwork6Ptr network(new SharedNetwork6("frog"));