]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#1254] Updated tests and doc
authorFrancis Dupont <fdupont@isc.org>
Thu, 2 Jul 2020 23:47:41 +0000 (01:47 +0200)
committerRazvan Becheriu <razvan@isc.org>
Fri, 14 Aug 2020 14:43:55 +0000 (17:43 +0300)
ChangeLog
doc/sphinx/arm/dhcp4-srv.rst
doc/sphinx/arm/dhcp6-srv.rst
src/bin/dhcp4/tests/host_unittest.cc
src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc
src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc

index 05a4a4adf60e0229a389fe4f91d9a1b580fad303..0df15559ff247eff3dbe5a66075289cc41602600 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+17xx.  [bug]*          fdupont
+       When a host reservation in a subnet reserves an address, the
+       address must in the subnet prefix. This check was previously
+       only done by the host command hook library. Note it does not
+       applies the prefix delegation.
+       (Gitlab #1254)
+
 1787.  [bug]           razvan
        The recount leases functions consider leases in 'declined' state as
        'assigned' so that when the lease is reclaimed or reused, no negative
index 2802df14ec0a218543ca46f0745f21674d597379..65eab79b7710c993ab9f268f52e875459c2a7e23 100644 (file)
@@ -3841,6 +3841,11 @@ In most cases an IP address will be specified. It is also possible to
 specify a hostname, host-specific options, or fields carried within the
 DHCPv4 message such as siaddr, sname, or file.
 
+.. note::
+
+   Beginning with Kea 1.7.11 the reserved address must be in the subnet
+   prefix.
+
 The following example shows how to reserve addresses for specific hosts
 in a subnet:
 
index 2c16ec8fcfc8fec1b9fa4daa84f00c698b607519..86a5b1fe85ef9da2b58df09e540b51afaf02fffa 100644 (file)
@@ -3354,6 +3354,11 @@ usually a DUID, but it can also be a hardware or MAC address. One or more
 addresses or prefixes may also be specified, and it is possible to
 specify a hostname and DHCPv6 options for a given host.
 
+.. note::
+
+   Beginning with Kea 1.7.11 all reserved addresses must be in the subnet
+   prefix. This does not apply to reserved prefixes.
+
 The following example shows how to reserve addresses and prefixes for
 specific hosts:
 
index 6ef2573a83611f205feca1510c652d08ffe00fc1..de109c606c480afc5f85d33c00fecdfb6144e4c4 100644 (file)
@@ -134,7 +134,7 @@ const char* CONFIGS[] = {
         "        {\n"
         "           \"hw-address\": \"aa:bb:cc:dd:ee:ff\",\n"
         "           \"hostname\": \"subnet-10-host\",\n"
-        "           \"ip-address\": \"192.0.5.10\"\n"
+        "           \"ip-address\": \"10.0.0.105\"\n"
         "        }]\n"
         "    }\n"
         "]\n"
@@ -154,14 +154,15 @@ const char* CONFIGS[] = {
         "\"subnet4\": [\n"
         "    {\n"
         "        \"subnet\": \"10.0.0.0/24\", \n"
-        "        \"id\": 10," "        \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ],\n"
+        "        \"id\": 10,"
+        "        \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ],\n"
         "        \"interface\": \"eth0\",\n"
         "        \"reservation-mode\": \"all\","
         "        \"reservations\": [ \n"
         "        {\n"
         "           \"hw-address\": \"aa:bb:cc:dd:ee:ff\",\n"
         "           \"hostname\": \"subnet-10-host\",\n"
-        "           \"ip-address\": \"192.0.5.10\"\n"
+        "           \"ip-address\": \"10.0.0.105\"\n"
         "        }]\n"
         "    }\n"
         "]\n"
@@ -550,9 +551,9 @@ TEST_F(HostTest, outOfPoolOverGlobal) {
     // Hardware address matches all reservations
     client.setHWAddress("aa:bb:cc:dd:ee:ff");
 
-    // Subnet 10 usses default HR mode(i.e. "in-pool"), so its
+    // Subnet 10 uses "out-of-pool" HR mode, so its
     // reservation should be used, rather than global.
-    runDoraTest(CONFIGS[2], client, "subnet-10-host", "192.0.5.10");
+    runDoraTest(CONFIGS[2], client, "subnet-10-host", "10.0.0.105");
 }
 
 // Verifies that when there are matching reservations at
@@ -565,9 +566,9 @@ TEST_F(HostTest, allOverGlobal) {
     // Hardware address matches all reservations
     client.setHWAddress("aa:bb:cc:dd:ee:ff");
 
-    // Subnet 10 usses default HR mode(i.e. "in-pool"), so its
+    // Subnet 10 uses default HR mode(i.e. "all"), so its
     // reservation should be used, rather than global.
-    runDoraTest(CONFIGS[3], client, "subnet-10-host", "192.0.5.10");
+    runDoraTest(CONFIGS[3], client, "subnet-10-host", "10.0.0.105");
 }
 
 // Verifies that client class specified in the global reservation
index 2e03260d5935646409a89454bb13301f329bb1d8..75b0ba2c2835c0ceae4906ab854f2343e6cda88a 100644 (file)
@@ -22,6 +22,8 @@
 #include <dhcpsrv/subnet.h>
 #include <dhcpsrv/subnet_id.h>
 #include <dhcpsrv/subnet_selector.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/cfg_hosts.h>
 #include <stats/stats_mgr.h>
 #include <testutils/gtest_utils.h>
 #include <testutils/test_to_element.h>
@@ -1900,5 +1902,60 @@ TEST(CfgSubnets4Test, removeStatistics) {
                                "reclaimed-leases"));
     ASSERT_FALSE(observation);
 }
+    
+// This test verifies that in range host reservation works as expected.
+TEST(CfgSubnets4Test, host) {
+    // Create a configuration.
+    std::string json =
+        "        {"
+        "            \"id\": 1,\n"
+        "            \"subnet\": \"10.1.2.0/24\", \n"
+        "            \"reservations\": [ {\n"
+        "                \"hw-address\": \"aa:bb:cc:dd:ee:ff\", \n"
+        "                \"ip-address\": \"10.1.2.1\" } ]\n"
+        "        }";
+
+    data::ElementPtr elems;
+    ASSERT_NO_THROW(elems = data::Element::fromJSON(json))
+        << "invalid JSON:" << json << "\n test is broken";
+
+    Subnet4ConfigParser parser;
+    Subnet4Ptr subnet;
+    EXPECT_NO_THROW(subnet = parser.parse(elems));
+    ASSERT_TRUE(subnet);
+    CfgHostsPtr cfg_hosts = CfgMgr::instance().getStagingCfg()->getCfgHosts();
+    ASSERT_TRUE(cfg_hosts);
+    HostCollection hosts = cfg_hosts->getAll4(SubnetID(1));
+    ASSERT_EQ(1, hosts.size());
+    ConstHostPtr host = hosts[0];
+    ASSERT_TRUE(host);
+    EXPECT_EQ(1, host->getIPv4SubnetID());
+    EXPECT_EQ("hwaddr=AABBCCDDEEFF", host->getIdentifierAsText());
+    EXPECT_EQ("10.1.2.1", host->getIPv4Reservation().toText());
+
+    CfgMgr::instance().clear();
+}
+
+// This test verifies that an out of range host reservation is rejected.
+TEST(CfgSubnets4Test, outOfRangeHost) {
+    // Create a configuration.
+    std::string json =
+        "        {"
+        "            \"id\": 1,\n"
+        "            \"subnet\": \"10.1.2.0/24\", \n"
+        "            \"reservations\": [ {\n"
+        "                \"hw-address\": \"aa:bb:cc:dd:ee:ff\", \n"
+        "                \"ip-address\": \"10.2.2.1\" } ]\n"
+        "        }";
+
+    data::ElementPtr elems;
+    ASSERT_NO_THROW(elems = data::Element::fromJSON(json))
+        << "invalid JSON:" << json << "\n test is broken";
+
+    Subnet4ConfigParser parser;
+    std::string msg = "specified reservation '10.2.2.1' is not matching ";
+    msg += "the IPv4 subnet prefix '10.1.2.0/24'";
+    EXPECT_THROW_MSG(parser.parse(elems), DhcpConfigError, msg);
+}
 
 } // end of anonymous namespace
index 35fb0c55b1a342ab5b7077c556a23d386c268a57..d32d8555d83b1b882d46627f813810462f7f8f8a 100644 (file)
@@ -18,6 +18,8 @@
 #include <dhcpsrv/subnet.h>
 #include <dhcpsrv/subnet_id.h>
 #include <dhcpsrv/subnet_selector.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/cfg_hosts.h>
 #include <stats/stats_mgr.h>
 #include <testutils/gtest_utils.h>
 #include <testutils/test_to_element.h>
@@ -1774,4 +1776,106 @@ TEST(CfgSubnets6Test, removeStatistics) {
     ASSERT_FALSE(observation);
 }
 
+// This test verifies that in range host reservation works as expected.
+TEST(CfgSubnets6Test, hostNA) {
+    // Create a configuration.
+    std::string json =
+        "        {"
+        "            \"id\": 1,\n"
+        "            \"subnet\": \"2001:db8:1::/48\", \n"
+        "            \"reservations\": [ {\n"
+        "                \"hw-address\": \"aa:bb:cc:dd:ee:ff\", \n"
+        "                \"ip-addresses\": [\"2001:db8:1::1\"] } ]\n"
+        "        }";
+
+    data::ElementPtr elems;
+    ASSERT_NO_THROW(elems = data::Element::fromJSON(json))
+        << "invalid JSON:" << json << "\n test is broken";
+
+    Subnet6ConfigParser parser;
+    Subnet6Ptr subnet;
+    EXPECT_NO_THROW(subnet = parser.parse(elems));
+    ASSERT_TRUE(subnet);
+    CfgHostsPtr cfg_hosts = CfgMgr::instance().getStagingCfg()->getCfgHosts();
+    ASSERT_TRUE(cfg_hosts);
+    HostCollection hosts = cfg_hosts->getAll6(SubnetID(1));
+    ASSERT_EQ(1, hosts.size());
+    ConstHostPtr host = hosts[0];
+    ASSERT_TRUE(host);
+    EXPECT_EQ(1, host->getIPv6SubnetID());
+    EXPECT_EQ("hwaddr=AABBCCDDEEFF", host->getIdentifierAsText());
+    IPv6ResrvRange addresses = host->getIPv6Reservations(IPv6Resrv::TYPE_NA);
+    ASSERT_EQ(1, std::distance(addresses.first, addresses.second));
+    EXPECT_EQ("2001:db8:1::1", addresses.first->second.getPrefix().toText());
+
+    CfgMgr::instance().clear();
+}
+
+// This test verifies that an out of range host reservation is rejected.
+TEST(CfgSubnets6Test, outOfRangeHost) {
+    // Create a configuration.
+    std::string json =
+        "        {"
+        "            \"id\": 1,\n"
+        "            \"subnet\": \"2001:db8:1::/48\", \n"
+        "            \"reservations\": [ {\n"
+        "                \"hw-address\": \"aa:bb:cc:dd:ee:ff\", \n"
+        "                \"ip-addresses\": [\"2001:db8:1::1\", \n"
+        "                                   \"2001:db8:2::1\"] } ]\n"
+        "        }";
+
+    data::ElementPtr elems;
+    ASSERT_NO_THROW(elems = data::Element::fromJSON(json))
+        << "invalid JSON:" << json << "\n test is broken";
+
+    Subnet6ConfigParser parser;
+    std::string msg = "specified reservation '2001:db8:2::1' is ";
+    msg += "not matching the IPv6 subnet prefix '2001:db8:1::/48'";
+    EXPECT_THROW_MSG(parser.parse(elems), DhcpConfigError, msg);
+}
+
+// This test verifies that in range validation does not applies to prefixes.
+TEST(CfgSubnets6Test, hostPD) {
+    // Create a configuration.
+    std::string json =
+        "        {"
+        "            \"id\": 1,\n"
+        "            \"subnet\": \"2001:db8:1::/48\", \n"
+        "            \"reservations\": [ {\n"
+        "                \"hw-address\": \"aa:bb:cc:dd:ee:ff\", \n"
+        "                \"prefixes\": [\"2001:db8:2::/64\"] } ]\n"
+        "        }";
+
+    data::ElementPtr elems;
+    ASSERT_NO_THROW(elems = data::Element::fromJSON(json))
+        << "invalid JSON:" << json << "\n test is broken";
+
+    Subnet6ConfigParser parser;
+    Subnet6Ptr subnet;
+    try {
+      subnet = parser.parse(elems);
+    } catch (const std::exception& ex) {
+      std::cerr << "parse fail with " << ex.what() << std::endl;
+    }
+    //EXPECT_NO_THROW(subnet = parser.parse(elems));
+    ASSERT_TRUE(subnet);
+    CfgHostsPtr cfg_hosts = CfgMgr::instance().getStagingCfg()->getCfgHosts();
+    ASSERT_TRUE(cfg_hosts);
+    HostCollection hosts = cfg_hosts->getAll6(SubnetID(1));
+    ASSERT_EQ(1, hosts.size());
+    ConstHostPtr host = hosts[0];
+    ASSERT_TRUE(host);
+    EXPECT_EQ(1, host->getIPv6SubnetID());
+    EXPECT_EQ("hwaddr=AABBCCDDEEFF", host->getIdentifierAsText());
+    IPv6ResrvRange prefixes = host->getIPv6Reservations(IPv6Resrv::TYPE_PD);
+    ASSERT_EQ(1, std::distance(prefixes.first, prefixes.second));
+    EXPECT_EQ("2001:db8:2::", prefixes.first->second.getPrefix().toText());
+    EXPECT_EQ(64, prefixes.first->second.getPrefixLen());
+
+    // Verify the prefix is not in the subnet range.
+    EXPECT_FALSE(subnet->inRange(prefixes.first->second.getPrefix()));
+
+    CfgMgr::instance().clear();
+}
+
 } // end of anonymous namespace