]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#2961] Checkpoint: updated code, tests and doc
authorFrancis Dupont <fdupont@isc.org>
Fri, 10 May 2024 23:18:33 +0000 (01:18 +0200)
committerFrancis Dupont <fdupont@isc.org>
Mon, 13 May 2024 16:23:59 +0000 (18:23 +0200)
14 files changed:
doc/sphinx/arm/dhcp4-srv.rst
doc/sphinx/arm/dhcp6-srv.rst
src/bin/dhcp4/json_config_parser.cc
src/bin/dhcp6/json_config_parser.cc
src/lib/dhcpsrv/parsers/dhcp_parsers.cc
src/lib/dhcpsrv/parsers/simple_parser4.cc
src/lib/dhcpsrv/parsers/simple_parser6.cc
src/lib/dhcpsrv/subnet.cc
src/lib/dhcpsrv/subnet.h
src/lib/dhcpsrv/tests/alloc_engine_utils.cc
src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc
src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc
src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc
src/lib/dhcpsrv/tests/subnet_unittest.cc

index 4c5fd4b78a07a4517db7dd91df4e729442535630..bda778069c4ae2742e5d5370770d7ea9990cdf0c 100644 (file)
@@ -1207,26 +1207,8 @@ IPv4 Subnet Identifier
 
 The subnet identifier (subnet ID) is a unique number associated with a particular
 subnet. In principle, it is used to associate clients' leases with their
-respective subnets. The server configuration should contain unique and stable
-identifiers for all subnets. When a subnet identifier is not specified for a
-subnet, it is automatically assigned by the configuration mechanism. The identifiers
-are assigned starting at 1 and are monotonically increased for each subsequent
-subnet: 1, 2, 3, ....
-
-If there are multiple subnets configured with auto-generated identifiers
-and one of them is removed, the subnet identifiers may be renumbered.
-For example: if there are four subnets and the third is removed, the
-last subnet will be assigned the identifier that the third subnet had
-before removal. As a result, the leases stored in the lease database for
-subnet 3 are now associated with subnet 4, something that may have
-unexpected consequences. It is one of the reasons why auto-generated subnet
-identifiers are deprecated starting from Kea version 2.4.0.
-
-.. note::
-
-   The auto-generation of the subnet identifiers will be removed in a future
-   release. Starting from Kea 2.4.0, a subnet without an ``id`` entry
-   or with the zero value raises a warning at the configuration time.
+respective subnets. The server configuration must contain unique and stable
+identifiers for all subnets.
 
 .. note::
 
@@ -1247,10 +1229,6 @@ to a newly configured subnet:
        ]
    }
 
-This identifier will not change for this subnet unless the ``id``
-parameter is removed or set to 0. The value of 0 forces auto-generation
-of the subnet identifier.
-
 .. _ipv4-subnet-prefix:
 
 IPv4 Subnet Prefix
index 07fdf23d65400a96de2fd9634f1302c2f55c3711..37e09fd34cccbc02cb8a6887fe1fdd3d7c6b9d6b 100644 (file)
@@ -988,26 +988,8 @@ IPv6 Subnet Identifier
 
 The subnet identifier (subnet ID) is a unique number associated with a particular
 subnet. In principle, it is used to associate clients' leases with their
-respective subnets. The server configuration should contain unique and stable
-identifiers for all subnets. When a subnet identifier is not specified for a
-subnet, it is automatically assigned by the configuration mechanism. The identifiers
-are assigned starting at 1 and are monotonically increased for each subsequent
-subnet: 1, 2, 3, ....
-
-If there are multiple subnets configured with auto-generated identifiers
-and one of them is removed, the subnet identifiers may be renumbered.
-For example: if there are four subnets and the third is removed, the
-last subnet will be assigned the identifier that the third subnet had
-before removal. As a result, the leases stored in the lease database for
-subnet 3 are now associated with subnet 4, something that may have
-unexpected consequences. It is one of the reasons why auto-generated subnet
-identifiers are deprecated starting from Kea version 2.4.0.
-
-.. note::
-
-   The auto-generation of the subnet identifiers will be removed in a future
-   release. Starting from Kea 2.4.0, a subnet without an ``id`` entry
-   or with the zero value raises a warning at the configuration time.
+respective subnets. The server configuration must contain unique and stable
+identifiers for all subnets.
 
 .. note::
 
@@ -1028,10 +1010,6 @@ to a newly configured subnet:
        ]
    }
 
-This identifier will not change for this subnet unless the ``id``
-parameter is removed or set to 0. The value of 0 forces auto-generation
-of the subnet identifier.
-
 .. _ipv6-subnet-prefix:
 
 IPv6 Subnet Prefix
index 6e203d7a6ed1987f3e361cfd8980ea47b8aa96ce..987bf3b5e03ff88973263fe66eecf94c4cc5bf7b 100644 (file)
@@ -327,10 +327,6 @@ void configureCommandChannel() {
 /// @param config_set the configuration being processed
 isc::data::ConstElementPtr
 processDhcp4Config(isc::data::ConstElementPtr config_set) {
-    // Before starting any subnet operations, let's reset the subnet-id counter,
-    // so newly recreated configuration starts with first subnet-id equal 1.
-    Subnet::resetSubnetID();
-
     // Revert any runtime option definitions configured so far and not committed.
     LibDHCP::revertRuntimeOptionDefs();
     // Let's set empty container in case a user hasn't specified any configuration
index 7783f4142ea8430c2393d43012edc7d881bebb96..71e265d56a217dadfc18fdb16e47aa18cb87c2ce 100644 (file)
@@ -429,10 +429,6 @@ void configureCommandChannel() {
 /// @param config_set the configuration being processed
 isc::data::ConstElementPtr
 processDhcp6Config(isc::data::ConstElementPtr config_set) {
-    // Before starting any subnet operations, let's reset the subnet-id counter,
-    // so newly recreated configuration starts with first subnet-id equal 1.
-    Subnet::resetSubnetID();
-
     // Revert any runtime option definitions configured so far and not committed.
     LibDHCP::revertRuntimeOptionDefs();
     // Let's set empty container in case a user hasn't specified any configuration
index 0af6f854b4a66cbbb598f2cfa02b16178896635e..724d2381cbdbd94d680f71dee122f918410bb9fc 100644 (file)
@@ -730,11 +730,9 @@ Subnet4ConfigParser::parse(ConstElementPtr subnet, bool encapsulate_options) {
 void
 Subnet4ConfigParser::initSubnet(data::ConstElementPtr params,
                                 asiolink::IOAddress addr, uint8_t len) {
-    // Subnet ID is optional. If it is not supplied the value of 0 is used,
-    // which means autogenerate. The value was inserted earlier by calling
-    // SimpleParser4::setAllDefaults.
+    // Subnet ID is required and must be in 1..SUBNET_ID_MAX.
     int64_t subnet_id_max = static_cast<int64_t>(SUBNET_ID_MAX);
-    SubnetID subnet_id = static_cast<SubnetID>(getInteger(params, "id", 0,
+    SubnetID subnet_id = static_cast<SubnetID>(getInteger(params, "id", 1,
                                                           subnet_id_max));
 
     auto subnet4 = Subnet4::create(addr, len, Triplet<uint32_t>(),
@@ -1259,11 +1257,9 @@ Subnet6ConfigParser::duplicateOptionWarning(uint32_t code,
 void
 Subnet6ConfigParser::initSubnet(data::ConstElementPtr params,
                                 asiolink::IOAddress addr, uint8_t len) {
-    // Subnet ID is optional. If it is not supplied the value of 0 is used,
-    // which means autogenerate. The value was inserted earlier by calling
-    // SimpleParser6::setAllDefaults.
+    // Subnet ID is required and must be in 1..SUBNET_ID_MAX.
     int64_t subnet_id_max = static_cast<int64_t>(SUBNET_ID_MAX);
-    SubnetID subnet_id = static_cast<SubnetID>(getInteger(params, "id", 0,
+    SubnetID subnet_id = static_cast<SubnetID>(getInteger(params, "id", 1,
                                                           subnet_id_max));
 
     // We want to log whether rapid-commit is enabled, so we get this
index 0a079abcf6d289027c67705f59a72f6698d4bf91..6c979bea335fc1bb021fe8a21046cc44a1fa6763 100644 (file)
@@ -270,7 +270,6 @@ const SimpleKeywords SimpleParser4::SUBNET4_PARAMETERS = {
 /// defined on global level. Currently there are two such parameters:
 /// interface and reservation-mode
 const SimpleDefaults SimpleParser4::SUBNET4_DEFAULTS = {
-    { "id",               Element::integer, "0" }, // 0 means autogenerate
     { "interface",        Element::string,  "" },
     { "client-class",     Element::string,  "" },
     { "4o6-interface",    Element::string,  "" },
@@ -285,7 +284,6 @@ const SimpleDefaults SimpleParser4::SUBNET4_DEFAULTS = {
 /// that can be derived from shared-network, but cannot from global scope.
 /// Those are: interface and reservation-mode.
 const SimpleDefaults SimpleParser4::SHARED_SUBNET4_DEFAULTS = {
-    { "id",               Element::integer, "0" }, // 0 means autogenerate
     { "4o6-interface",    Element::string,  "" },
     { "4o6-interface-id", Element::string,  "" },
     { "4o6-subnet",       Element::string,  "" },
index 97c590ce15b9098c75216a7a0f421c11d206c653..f31c5d53c844a9c5b2b493da86c35bec1b96c7b4 100644 (file)
@@ -261,7 +261,6 @@ const SimpleKeywords SimpleParser6::SUBNET6_PARAMETERS = {
 /// where a parameter can be derived from shared-networks, but is not
 /// defined on global level.
 const SimpleDefaults SimpleParser6::SUBNET6_DEFAULTS = {
-    { "id",               Element::integer, "0" }, // 0 means autogenerate
     { "interface",        Element::string,  "" },
     { "client-class",     Element::string,  "" },
     { "rapid-commit",     Element::boolean, "false" }, // rapid-commit disabled by default
@@ -274,7 +273,6 @@ const SimpleDefaults SimpleParser6::SUBNET6_DEFAULTS = {
 /// This is mostly the same as @ref SUBNET6_DEFAULTS, except the parameters
 /// that can be derived from shared-network, but cannot from global scope.
 const SimpleDefaults SimpleParser6::SHARED_SUBNET6_DEFAULTS = {
-    { "id",               Element::integer, "0" } // 0 means autogenerate
 };
 
 /// @brief This table defines default values for each IPv6 shared network.
index f500e95222d93e362a0844f7304d8d6de473299f..9988b0f8756e48b96096fea5018ef8f67761ae83 100644 (file)
@@ -65,21 +65,14 @@ comparePoolFirstAddress(const PoolPtr& pool1,
 namespace isc {
 namespace dhcp {
 
-// This is an initial value of subnet-id. See comments in subnet.h for details.
-SubnetID Subnet::static_id_ = 1;
-
 Subnet::Subnet(const isc::asiolink::IOAddress& prefix, uint8_t len,
                const SubnetID id)
-    : id_(id == 0 ? generateNextID() : id), prefix_(prefix),
-      prefix_len_(len),
-      shared_network_name_() {
-    if ((id == 0) && (id_ == 1)) {
-        // Emit a warning on the first auto-numbered subnet.
-        LOG_WARN(dhcpsrv_logger, DHCPSRV_CONFIGURED_SUBNET_WITHOUT_ID)
-            .arg(toText());
-    }
-    if ((prefix.isV6() && len > 128) ||
-        (prefix.isV4() && len > 32)) {
+    : id_(id), prefix_(prefix), prefix_len_(len), shared_network_name_() {
+    if ((id == SUBNET_ID_GLOBAL) || (id == SUBNET_ID_UNUSED)) {
+        isc_throw(BadValue,
+                  "Invalid id specified for subnet: " << id);
+    }
+    if ((prefix.isV6() && len > 128) || (prefix.isV4() && len > 32)) {
         isc_throw(BadValue,
                   "Invalid prefix length specified for subnet: " << len);
     }
index 54e18d3e586f4625ee70eb0ab73037216e20b40e..c93c7ed7773b4fc5eb81dacb7e4f260090aef7bd 100644 (file)
@@ -211,15 +211,6 @@ public:
     /// @return textual representation
     virtual std::string toText() const;
 
-    /// @brief Resets subnet-id counter to its initial value (1).
-    ///
-    /// This should be called during reconfiguration, before any new
-    /// subnet objects are created. It will ensure that the subnet_id will
-    /// be consistent between reconfigures.
-    static void resetSubnetID() {
-        static_id_ = 1;
-    }
-
     /// @brief Retrieves pointer to a shared network associated with a subnet.
     ///
     /// By implementing it as a template function we overcome a need to
@@ -338,15 +329,9 @@ protected:
     /// By making the constructor protected, we make sure that no one will
     /// ever instantiate that class. Subnet4 and Subnet6 should be used instead.
     ///
-    /// This constructor assigns a new subnet-id (see @ref generateNextID).
-    /// This subnet-id has unique value that is strictly monotonously increasing
-    /// for each subnet, until it is explicitly reset back to 1 during
-    /// reconfiguration process.
-    ///
     /// @param prefix subnet prefix
     /// @param len prefix length for the subnet
-    /// @param id arbitrary subnet id, value of 0 triggers autogeneration
-    /// of subnet id
+    /// @param id arbitrary subnet id between 0 and 2^32-1 excluded.
     Subnet(const isc::asiolink::IOAddress& prefix, uint8_t len,
            const SubnetID id);
 
@@ -356,31 +341,6 @@ protected:
     /// derive from this class.
     virtual ~Subnet() { };
 
-    /// @brief keeps the subnet-id value.
-    ///
-    /// It is incremented every time a new Subnet object is created. It is reset
-    /// (@ref resetSubnetID) every time reconfiguration
-    /// occurs.
-    ///
-    /// Static value initialized in subnet.cc.
-    static SubnetID static_id_;
-
-    /// @brief returns the next unique Subnet-ID.
-    ///
-    /// This method generates and returns the next unique subnet-id.
-    /// It is a strictly monotonously increasing value (1,2,3,...) for
-    /// each new Subnet object created. It can be explicitly reset
-    /// back to 1 during reconfiguration (@ref resetSubnetID).
-    ///
-    /// @return the next unique Subnet-ID
-    static SubnetID generateNextID() {
-        if (static_id_ == SUBNET_ID_MAX) {
-            resetSubnetID();
-        }
-
-        return (static_id_++);
-    }
-
     /// @brief Checks if used pool type is valid.
     ///
     /// Allowed type for Subnet4 is Pool::TYPE_V4.
index 422222f608088ec42902b24757adbb26b7459096..cab3c9386ceb8a30a84c29672d48e0e7cf43981a 100644 (file)
@@ -171,9 +171,6 @@ AllocEngine4Test::generateDeclinedLease(const std::string& addr,
 }
 
 AllocEngine6Test::AllocEngine6Test() {
-    // No longer used but this means too that tests relied far too much on it.
-    //Subnet::resetSubnetID();
-
     CfgMgr::instance().clear();
 
     // This lease mgr needs to exist to before configuration commits.
@@ -649,9 +646,6 @@ AllocEngine4Test::initSubnet(const asiolink::IOAddress& pool_start,
 }
 
 AllocEngine4Test::AllocEngine4Test() {
-    // No longer used but this means too that tests relied far too much on it.
-    //Subnet::resetSubnetID();
-
     CfgMgr::instance().clear();
 
     // This lease mgr needs to exist to before configuration commits.
index 9e3befd2db413c2d7ea6657c11fb8ab749194149..2e1540715921d0172d8d99d6c32f951e76f7b4b3 100644 (file)
@@ -1298,6 +1298,78 @@ TEST(CfgSubnets4Test, hasSubnetWithServerId) {
     EXPECT_FALSE(cfg.hasSubnetWithServerId(IOAddress("2.3.4.5")));
 }
 
+// This test verifies the Subnet4 parser's validation logic for id parameter.
+TEST(CfgSubnets4Test, idValidation) {
+    {
+        // id 0.
+        SCOPED_TRACE("id 0");
+        std::string json =
+            "        {\n"
+            "            \"id\": 0,\n"
+            "            \"subnet\": \"10.1.2.0/24\"\n"
+            "        }\n";
+        data::ElementPtr elems;
+        ASSERT_NO_THROW(elems = data::Element::fromJSON(json))
+            << "invalid JSON:" << json  << "\n test is broken";
+        std::string expected = "subnet configuration failed: ";
+        expected += "The 'id' value (0) is not within ";
+        expected += "expected range: (1 - 4294967294)";
+        try {
+            // Attempt to parse the configuration.
+            Subnet4ConfigParser parser;
+            Subnet4Ptr subnet = parser.parse(elems);
+            ADD_FAILURE() << "expected exception";
+        } catch (const std::exception& ex) {
+            EXPECT_EQ(expected, ex.what());
+        }
+    }
+
+    {
+        // id 4294967295.
+        SCOPED_TRACE("id 4294967295");
+        std::string json =
+            "        {\n"
+            "            \"id\": 4294967295,\n"
+            "            \"subnet\": \"10.1.2.0/24\"\n"
+            "        }\n";
+        data::ElementPtr elems;
+        ASSERT_NO_THROW(elems = data::Element::fromJSON(json))
+            << "invalid JSON:" << json  << "\n test is broken";
+        std::string expected = "subnet configuration failed: ";
+        expected += "The 'id' value (4294967295) is not within ";
+        expected += "expected range: (1 - 4294967294)";
+        try {
+            // Attempt to parse the configuration.
+            Subnet4ConfigParser parser;
+            Subnet4Ptr subnet = parser.parse(elems);
+            ADD_FAILURE() << "expected exception";
+        } catch (const std::exception& ex) {
+            EXPECT_EQ(expected, ex.what());
+        }
+    }
+
+    {
+        // id 1 (valid).
+        SCOPED_TRACE("id 1");
+        std::string json =
+            "        {\n"
+            "            \"id\": 1,\n"
+            "            \"subnet\": \"10.1.2.0/24\"\n"
+            "        }\n";
+        data::ElementPtr elems;
+        ASSERT_NO_THROW(elems = data::Element::fromJSON(json))
+            << "invalid JSON:" << json  << "\n test is broken";
+        try {
+            // Attempt to parse the configuration.
+            Subnet4ConfigParser parser;
+            Subnet4Ptr subnet = parser.parse(elems);
+            EXPECT_TRUE(subnet);
+        } catch (const std::exception& ex) {
+            ADD_FAILURE() << "unexpected failure " << ex.what();
+        }
+    }
+}
+
 // This test verifies the Subnet4 parser's validation logic for
 // t1-percent and t2-percent parameters.
 TEST(CfgSubnets4Test, teeTimePercentValidation) {
index 3a847327db5fcf0d74705bb776531792defb2e7d..38f8b255e02f8b4e18323ecbe26918c276303de6 100644 (file)
@@ -1102,6 +1102,78 @@ TEST(CfgSubnets6Test, mergeSubnets) {
     EXPECT_EQ("RULE!", opstr->getValue());
 }
 
+// This test verifies the Subnet6 parser's validation logic for id parameter.
+TEST(CfgSubnets6Test, idValidation) {
+    {
+        // id 0.
+        SCOPED_TRACE("id 0");
+        std::string json =
+            "        {\n"
+            "            \"id\": 0,\n"
+            "            \"subnet\": \"2001:db8:1::/64\"\n"
+            "        }\n";
+        data::ElementPtr elems;
+        ASSERT_NO_THROW(elems = data::Element::fromJSON(json))
+            << "invalid JSON:" << json  << "\n test is broken";
+        std::string expected = "subnet configuration failed: ";
+        expected += "The 'id' value (0) is not within ";
+        expected += "expected range: (1 - 4294967294)";
+        try {
+            // Attempt to parse the configuration.
+            Subnet6ConfigParser parser;
+            Subnet6Ptr subnet = parser.parse(elems);
+            ADD_FAILURE() << "expected exception";
+        } catch (const std::exception& ex) {
+            EXPECT_EQ(expected, ex.what());
+        }
+    }
+
+    {
+        // id 4294967295.
+        SCOPED_TRACE("id 4294967295");
+        std::string json =
+            "        {\n"
+            "            \"id\": 4294967295,\n"
+            "            \"subnet\": \"2001:db8:1::/64\"\n"
+            "        }\n";
+        data::ElementPtr elems;
+        ASSERT_NO_THROW(elems = data::Element::fromJSON(json))
+            << "invalid JSON:" << json  << "\n test is broken";
+        std::string expected = "subnet configuration failed: ";
+        expected += "The 'id' value (4294967295) is not within ";
+        expected += "expected range: (1 - 4294967294)";
+        try {
+            // Attempt to parse the configuration.
+            Subnet6ConfigParser parser;
+            Subnet6Ptr subnet = parser.parse(elems);
+            ADD_FAILURE() << "expected exception";
+        } catch (const std::exception& ex) {
+            EXPECT_EQ(expected, ex.what());
+        }
+    }
+
+    {
+        // id 1 (valid).
+        SCOPED_TRACE("id 1");
+        std::string json =
+            "        {\n"
+            "            \"id\": 1,\n"
+            "            \"subnet\": \"2001:db8:1::/64\"\n"
+            "        }\n";
+        data::ElementPtr elems;
+        ASSERT_NO_THROW(elems = data::Element::fromJSON(json))
+            << "invalid JSON:" << json  << "\n test is broken";
+        try {
+            // Attempt to parse the configuration.
+            Subnet6ConfigParser parser;
+            Subnet6Ptr subnet = parser.parse(elems);
+            EXPECT_TRUE(subnet);
+        } catch (const std::exception& ex) {
+            ADD_FAILURE() << "unexpected failure " << ex.what();
+        }
+    }
+}
+
 // This test verifies the Subnet6 parser's validation logic for
 // t1-percent and t2-percent parameters.
 TEST(CfgSubnets6Test, teeTimePercentValidation) {
index e9731b266ce0b859ca6602961621efb368bc4655..c11891bb23bc5cbab53c37bead370340649d4068 100644 (file)
@@ -3129,7 +3129,7 @@ TEST_F(ParseConfigTest, negativeSubnetId4) {
     std::string expected = "Configuration parsing failed: ";
     expected += "subnet configuration failed: ";
     expected += "The 'id' value (-1) is not within expected range: ";
-    expected += "(0 - 4294967294)";
+    expected += "(1 - 4294967294)";
     EXPECT_EQ(expected, comment->stringValue());
 }
 
@@ -3154,7 +3154,7 @@ TEST_F(ParseConfigTest, negativeSubnetId6) {
     std::string expected = "Configuration parsing failed: ";
     expected += "subnet configuration failed: ";
     expected += "The 'id' value (-1) is not within expected range: ";
-    expected += "(0 - 4294967294)";
+    expected += "(1 - 4294967294)";
     EXPECT_EQ(expected, comment->stringValue());
 }
 
@@ -3179,7 +3179,7 @@ TEST_F(ParseConfigTest, reservedSubnetId4) {
     std::string expected = "Configuration parsing failed: ";
     expected += "subnet configuration failed: ";
     expected += "The 'id' value (4294967295) is not within expected range: ";
-    expected += "(0 - 4294967294)";
+    expected += "(1 - 4294967294)";
     EXPECT_EQ(expected, comment->stringValue());
 }
 
@@ -3204,7 +3204,7 @@ TEST_F(ParseConfigTest, reservedSubnetId6) {
     std::string expected = "Configuration parsing failed: ";
     expected += "subnet configuration failed: ";
     expected += "The 'id' value (4294967295) is not within expected range: ";
-    expected += "(0 - 4294967294)";
+    expected += "(1 - 4294967294)";
     EXPECT_EQ(expected, comment->stringValue());
 }
 
index 085b0d11a282f78a5066199e5f876cad3f4ed8fd..8b7ecce3a9ac4aaaac817fd85dd844ddd52e317f 100644 (file)
@@ -41,6 +41,13 @@ TEST(Subnet4Test, constructor) {
     EXPECT_NO_THROW(Subnet4 subnet1(IOAddress("192.0.2.2"), 16,
                                     1, 2, 3, 10));
 
+    EXPECT_THROW(Subnet4 subnet0(IOAddress("192.0.2.0"), 16,
+                                 1, 2, 3, SUBNET_ID_GLOBAL),
+                 BadValue); // invalid id (0).
+    EXPECT_THROW(Subnet4 subnetm(IOAddress("192.0.2.0"), 16,
+                                 1, 2, 3, SUBNET_ID_UNUSED),
+                 BadValue); // invalid id (max).
+
     EXPECT_THROW(Subnet4 subnet2(IOAddress("192.0.2.0"),
                                  33, 1, 2, 3, SubnetID(2)),
                 BadValue); // invalid prefix length
@@ -831,6 +838,13 @@ TEST(Subnet6Test, constructor) {
     EXPECT_NO_THROW(Subnet6 subnet1(IOAddress("2001:db8:1::"), 64,
                                     1, 2, 3, 4, SubnetID(1)));
 
+    EXPECT_THROW(Subnet6 subnet0(IOAddress("2001:db8:1::"), 64,
+                                 1, 2, 3, 4, SUBNET_ID_GLOBAL),
+                 BadValue); // invalid id (0).
+    EXPECT_THROW(Subnet6 subnetm(IOAddress("2001:db8:1::"), 64,
+                                 1, 2, 3, 4, SUBNET_ID_UNUSED),
+                 BadValue); // invalid id (max).
+
     EXPECT_THROW(Subnet6 subnet2(IOAddress("2001:db8:1::"),
                                  129, 1, 2, 3, 4, SubnetID(2)),
                 BadValue); // invalid prefix length
@@ -1954,87 +1968,4 @@ TEST(SubnetFetcherTest, getSubnet6ById) {
     EXPECT_EQ("2001:db8:2::/64", subnet->toText());
 }
 
-// Test fixture for subnet identifier auto-generation.
-class SubnetIdTest : public LogContentTest {
-public:
-
-    /// @brief virtual destructor.
-    virtual ~SubnetIdTest() {
-        Subnet::resetSubnetID();
-    }
-};
-
-// Test class for subnets with id = 0.
-class TestSubnet : public Subnet {
-public:
-    // @brief Constructor.
-    //
-    // @param prefix subnet prefix.
-    // @param len prefix length for the subnet.
-    TestSubnet(const IOAddress& prefix, uint8_t len)
-        : Subnet(prefix, len, 0) {
-    }
-
-    // @brief Returns the default address that will be used for pool selection.
-    virtual IOAddress default_pool() const {
-        isc_throw(NotImplemented, "default_pool");
-    }
-
-    /// @brief Instantiates the allocators and their states.
-    virtual void createAllocators() {
-        isc_throw(NotImplemented, "createAllocators");
-    }
-
-    /// @brief Checks if used pool type is valid.
-    virtual void checkType(Lease::Type type) const {
-        isc_throw(NotImplemented, "checkType " << type);
-    }
-};
-
-// Type of pointers to test subnets.
-typedef boost::shared_ptr<TestSubnet> TestSubnetPtr;
-
-// Test subnet identifier auto-generation.
-TEST_F(SubnetIdTest, unnumbered) {
-    // Reset subnet identifier counter.
-    Subnet::resetSubnetID();
-
-    // First subnet.
-    IOAddress addr1("192.0.2.0");
-    uint8_t len1(25);
-    TestSubnetPtr subnet1;
-    ASSERT_NO_THROW(subnet1.reset(new TestSubnet(addr1, len1)));
-    ASSERT_TRUE(subnet1);
-    EXPECT_EQ(1, subnet1->getID());
-    EXPECT_EQ("192.0.2.0/25", subnet1->toText());
-
-    // Second subnet.
-    IOAddress addr2("192.0.2.128");
-    uint8_t len2(25);
-    TestSubnetPtr subnet2;
-    ASSERT_NO_THROW(subnet2.reset(new TestSubnet(addr2, len2)));
-    ASSERT_TRUE(subnet2);
-    EXPECT_EQ(2, subnet2->getID());
-    EXPECT_EQ("192.0.2.128/25", subnet2->toText());
-
-    // Reset subnet identifier counter again to get another log.
-    Subnet::resetSubnetID();
-
-    // Third subnet.
-    IOAddress addr3("2001:db8:1::");
-    uint8_t len3(64);
-    TestSubnetPtr subnet3;
-    ASSERT_NO_THROW(subnet3.reset(new TestSubnet(addr3, len3)));
-    ASSERT_TRUE(subnet3);
-    EXPECT_EQ(1, subnet3->getID());
-    EXPECT_EQ("2001:db8:1::/64", subnet3->toText());
-
-    // Subnet 1 and 3 are logged.
-    std::string msg = "DHCPSRV_CONFIGURED_SUBNET_WITHOUT_ID ";
-    msg += "a subnet was configured without an id: ";
-    addString(msg + subnet1->toText());
-    addString(msg + subnet3->toText());
-    EXPECT_TRUE(checkFile());
-}
-
 }