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::
]
}
-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
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::
]
}
-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
/// @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
/// @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
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>(),
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
/// 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, "" },
/// 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, "" },
/// 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
/// 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.
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);
}
/// @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
/// 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);
/// 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.
}
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.
}
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.
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) {
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) {
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());
}
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());
}
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());
}
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());
}
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
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
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());
-}
-
}