<title>Sanity checks in DHCPv6</title>
<para>
- The sanity checks mechanism works exactly the same as its
- DHCPv4 counterpart. See <xref linkend="sanity-checks4"/> for
- details.
+ An important aspect of a well running DHCP system is an assurance that
+ the data remains consisent. However, in some cases it may be convenient
+ to tolerate certain inconsistent data. For example, a network
+ administrator that temporarily removed a subnet from a configuration
+ wouldn't want all the leases associated with it disappear from the
+ lease database. Kea 1.5 introduced a mechanism to better control sanity
+ checks such as this. While currently the scope of configurable sanity
+ checks is limited and their default value is set low, it is expected
+ that over time the default settings will be set to more aggressive
+ values and more parameters of similar nature will be added in the
+ future.
</para>
<para>
- The only difference is that the sanity checks mechanism works
- for address leases only. Since delegated prefixes do not have
- to belong to a subnet in which they're offered, there is no
- way to implement such a mechanism for IPv6 prefixes.
+ Kea now supports a new configuration scope called
+ <command>sanity-checks</command>. It currently allows only a
+ single parameter called <command>lease-checks</command>. It
+ governs what sort of verification is done when a new lease is
+ being loaded from a lease file. With the introduction of
+ sanity checks mechanism, it is now possible to tell Kea to
+ try to correct inconsistent data.
</para>
- <para>This feature is currently implemented for memfile backend and for
- lease6-add command.</para>
+ <para>
+ Every subnet has a subnet-id value. This is how Kea internally
+ identifies subnets. Each lease has a subnet-id parameter as well, which
+ identifies which subnet it belongs to. However, if configuration has
+ changed, it is possible that a lease could exist with a subnet-id
+ without any subnet that matches it. Also, it may be possible that
+ subnets configuration has changed and the subnet-id now belongs to a
+ subnet that does not match the lease. Kea corrective algorithm first
+ checks if there is a subnet with subnet-id specified by the lease. If
+ there is, it checks whether the lease belongs to that subnet. If not,
+ depending on the lease-checks setting, the lease is discarded, a
+ warning is printed or a new subnet is selected for the lease that
+ matches it topologically.
+ </para>
+
+ <para>
+ Since delegated prefixes do not have to belong to a subnet in which
+ they're offered, there is no way to implement such a mechanism for IPv6
+ prefixes. As such, the mechanism works for IPv6 addresses only.
+ </para>
- <para>
+ <para>
+ There are five levels which are supported:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <simpara><command>none</command> - do no special checks, accept the
+ lease as is</simpara>
+ </listitem>
+ <listitem>
+ <simpara><command>warn</command> - if problems are detected, a
+ warning will be printed, but the lease data will be accepted
+ anyway. This is the default value. If not explicitly configured to
+ some other value, this level will be used.</simpara>
+ </listitem>
+ <listitem>
+ <simpara><command>fix</command> - If data inconsistency is
+ discovered, Kea will try to correct it. If the correction is
+ not successful, the data will be inserted anyway.</simpara>
+ </listitem>
+ <listitem>
+ <simpara><command>fix-del</command> - If data inconsistency is
+ discovered, Kea will try to correct it. If the correction is not
+ succesful, the lease will be rejected. This setting ensures the data
+ correctness, but some incorrect data may be lost. Use with
+ care.</simpara>
+ </listitem>
+ <listitem>
+ <simpara><command>del</command> - This is the strictest mode. If any
+ inconsistency is detected, the lease is rejected. Use with care.
+ </simpara>
+ </listitem>
+ </itemizedlist>
+
+ <para>This feature is currently implemented for memfile backend.</para>
+
+ <para>
An example configuration that sets this parameter looks as follows:
<screen>
"Dhcp6": {
/// IO object to be used to write to this file.
LeaseFileLoaderTest();
+ /// @brief Destructor
+ ///
+ /// Removes any configuration that may have been added in CfgMgr.
+ ~LeaseFileLoaderTest();
+
/// @brief Prepends the absolute path to the file specified
/// as an argument.
///
Lease4Storage storage4_; ///< Storage for IPv4 leases
Lease6Storage storage6_; ///< Storage for IPv4 leases
+ /// @brief Creates IPv4 subnet with specified parameters
+ ///
+ /// @param subnet_txt subnet in textual form, e.g. 192.0.2.0/24
+ /// @param subnet_id id to be used.
+ /// @return A pointer to Subnet4 object
+ Subnet4Ptr createSubnet4(std::string subnet_txt, SubnetID id) {
+ size_t pos = subnet_txt.find("/");
+ isc::asiolink::IOAddress addr(subnet_txt.substr(0, pos));
+ size_t len = boost::lexical_cast<unsigned int>(subnet_txt.substr(pos + 1));
+
+ return (Subnet4Ptr(new Subnet4(addr, len, 1000, 2000, 3000, id)));
+ }
+
+ /// @brief Creates IPv6 subnet with specified parameters
+ ///
+ /// @param subnet_txt subnet in textual form, e.g. 2001:db8::/32
+ /// @param subnet_id id to be used.
+ /// @return A pointer to Subnet4 object
+ Subnet6Ptr createSubnet6(std::string subnet_txt, SubnetID id) {
+ size_t pos = subnet_txt.find("/");
+ isc::asiolink::IOAddress addr(subnet_txt.substr(0, pos));
+ size_t len = boost::lexical_cast<unsigned int>(subnet_txt.substr(pos + 1));
+
+ return (Subnet6Ptr(new Subnet6(addr, len, 1000, 2000, 3000, 4000, id)));
+ }
+
/// @brief Checks if IPv4 lease loaded from file is sanity checked.
///
/// This method writes a simple lease file with one lease in it,
///
/// @param lease address of the lease in text form
/// @param lease_id subnet-id to be used in a lease
+ /// @param subnet_txt Text representation of the subnet, e.g. 192.0.2.0/24
+ /// @param subnet_id Subnet-id of the subnet to be created
/// @param sanity level of sanity checks
/// @param exp_present is the lease expected to be loaded (true = yes)
/// @param exp_id expected subnet-id of the loaded lease
void sanityChecks4(std::string lease, SubnetID lease_id,
+ std::string subnet_txt, SubnetID subnet_id,
CfgConsistency::LeaseSanity sanity,
bool exp_present, SubnetID exp_id) {
+ // Create the subnet and add it to configuration.
+ if (!subnet_txt.empty()) {
+ Subnet4Ptr subnet = createSubnet4(subnet_txt, subnet_id);
+ ASSERT_NO_THROW(CfgMgr::instance().getStagingCfg()->getCfgSubnets4()->add(subnet));
+ }
+
std::stringstream file_content;
file_content << v4_hdr_ << lease << ",dd:de:ba:0d:1b:2e,"
<< "0a:00:01:04,100,100," << static_cast<int>(lease_id)
if (exp_present) {
ASSERT_TRUE(l) << "lease not found, but expected";
- EXPECT_EQ(exp_id, l->subnet_id_);
+ EXPECT_EQ(l->subnet_id_, exp_id);
} else {
EXPECT_FALSE(l) << "lease found, but was not expected";
}
LeaseFileLoaderTest::LeaseFileLoaderTest()
: filename_(absolutePath("leases4.csv")), io_(filename_) {
+ CfgMgr::instance().clear();
+}
+
+LeaseFileLoaderTest::~LeaseFileLoaderTest() {
+ CfgMgr::instance().clear();
}
std::string
// This test checks if the lease can be loaded, even though there are no
// subnets configured that it would match.
+// Scenario: print a warning, there's no subnet,
+// expected outcome: add the lease as is
TEST_F(LeaseFileLoaderTest, sanityChecker4NoSubnetsWarn) {
- sanityChecks4("192.0.2.1", 1, CfgConsistency::LEASE_CHECK_WARN, true, 1);
+ sanityChecks4("192.0.2.1", 1, "", 0, CfgConsistency::LEASE_CHECK_WARN, true, 1);
}
-// This test checks if the lease can be discarded. Note we are
-// verifying whether the checks are conducted at all when loading a file.
-// Thorough tests were conducted in sanity_checks_unittest.cc.
+// This test checks if the lease can be fixed.
+// Scenario: try to fix the lease, there's no subnet,
+// expected outcome: add the lease as is
+TEST_F(LeaseFileLoaderTest, sanityChecker4NoSubnetsFix) {
+ sanityChecks4("192.0.2.1", 1, "", 0, CfgConsistency::LEASE_CHECK_FIX, true, 1);
+}
+
+// This test checks if the lease can be discarded if it's impossible to fix.
+// Scenario: try to fix the lease, there's no subnet,
+// expected outcome: the lease is not loaded
+TEST_F(LeaseFileLoaderTest, sanityChecker4NoSubnetsFixDel) {
+ sanityChecks4("192.0.2.1", 1, "", 0, CfgConsistency::LEASE_CHECK_FIX_DEL, false, 1);
+}
+
+// This test checks if the lease can be discarded.
+// Scenario: try to fix the lease, there's no subnet,
+// expected outcome: the lease is not loaded
TEST_F(LeaseFileLoaderTest, sanityChecker4NoSubnetsDel) {
- sanityChecks4("192.0.2.1", 1, CfgConsistency::LEASE_CHECK_DEL, false, 1);
+ sanityChecks4("192.0.2.1", 1, "", 0, CfgConsistency::LEASE_CHECK_DEL, false, 1);
+}
+
+// This test checks if the lease can be fixed.
+// Scenario: try to fix the lease, there's a subnet,
+// expected outcome: correct the lease
+TEST_F(LeaseFileLoaderTest, sanityChecker4Fix) {
+ sanityChecks4("192.0.2.1", 1, "192.0.2.0/24", 2, CfgConsistency::LEASE_CHECK_FIX, true, 2);
+}
+
+// This test checks if the lease can be fixed when it's possible.
+// Scenario: try to fix the lease, there's a subnet,
+// expected outcome: the lease is not loaded
+TEST_F(LeaseFileLoaderTest, sanityChecker4FixDel1) {
+ sanityChecks4("192.0.2.1", 1, "192.0.2.0/24", 2, CfgConsistency::LEASE_CHECK_FIX_DEL, true, 2);
}
+// This test checks if the lease is discarded, when fix is not possible.
+// Scenario: try to fix the lease, there's a subnet, but it doesn't match,
+// expected outcome: the lease is not loaded
+TEST_F(LeaseFileLoaderTest, sanityChecker4FixDel2) {
+ sanityChecks4("192.0.2.1", 1, "192.0.3.0/24", 2, CfgConsistency::LEASE_CHECK_FIX_DEL, false, 1);
+}
+
+// This test checks if the lease is discarded.
+// Scenario: delete the lease, there's a subnet,
+// expected outcome: the lease is not loaded
+TEST_F(LeaseFileLoaderTest, sanityChecker4Del) {
+ sanityChecks4("192.0.2.1", 1, "192.0.2.0/24", 2, CfgConsistency::LEASE_CHECK_DEL, false, 1);
+}
+
+
+
+
// This test checks if the lease can be loaded, even though there are no
// subnets configured that it would match.
TEST_F(LeaseFileLoaderTest, sanityChecker6NoSubnetsWarn) {
EXPECT_EQ(cfg.getConsistency()->getLeaseSanityCheck(), exp_sanity);
}
- /// @brief Creates subnet configuration, inserts a lease and checks the result.
- ///
- /// This is a bit complicated test. It creates a Subnet configuration first
- /// (defined by subnet/subnet_id), adds it to the current configuration,
- /// then sets the lease checks to specified value, then creates a lease
- /// (addr/lease_id) and tries to add it to the lease backend (memfile is used).
- /// Afterwards it checks whether the lease is added or not (exp_lease_present)
- /// and if it is present, whether it has expected subnet-id (expected_id).
- ///
- /// @param addr Address of the lease to be created
- /// @param lease_id Subnet-id of the lease to be created
- /// @param subnet_txt Text representation of the subnet, e.g. 192.0.2.0/24
- /// @param subnet_id Subnet-id of the subnet to be created
- /// @param sanity Sanity checks set in the configuration
- /// @param exp_lease_present whether the lease is expected to be inserted
- /// @param expected_id if exp_lease_present is true, lease's subnet id should
- /// match this value.
- /// @return lease from the backend (may return null)
- void
- leaseAddCheck4(string addr, SubnetID lease_id, string subnet_txt, SubnetID subnet_id,
- CfgConsistency::LeaseSanity sanity, bool exp_lease_present,
- SubnetID expected_id) {
-
- // Let's start a backend in proper universe.
- startLeaseBackend(false);
-
- // Now set the sanity checks to a proper value.
- ASSERT_NO_THROW(CfgMgr::instance().getCurrentCfg()->getConsistency()
- ->setLeaseSanityCheck(sanity));
-
- // Create the subnet and add it to configuration.
- if (!subnet_txt.empty()) {
- Subnet4Ptr subnet = createSubnet4(subnet_txt, subnet_id);
- ASSERT_NO_THROW(CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->add(subnet));
- }
-
- // Now try to insert the lease.
- Lease4Ptr l = newLease4(addr, lease_id);
- bool result = false;
- EXPECT_NO_THROW(result = LeaseMgrFactory::instance().addLease(l));
- EXPECT_EQ(result, exp_lease_present);
-
- Lease4Ptr from_backend;
- if (result && exp_lease_present) {
-
- ASSERT_TRUE(from_backend = LeaseMgrFactory::instance().getLease4(IOAddress(addr)));
-
- EXPECT_EQ(l->addr_, from_backend->addr_);
- EXPECT_EQ(from_backend->subnet_id_, expected_id);
- }
- }
-
- /// @brief Creates subnet configuration, inserts a lease and checks the result.
- ///
- /// This is a bit complicated test. It creates a Subnet configuration first
- /// (defined by subnet/subnet_id), adds it to the current configuration,
- /// then sets the lease checks to specified value, then creates a lease
- /// (addr/lease_id) and tries to add it to the lease backend (memfile is used).
- /// Afterwards it checks whether the lease is added or not (exp_lease_present)
- /// and if it is present, whether it has expected subnet-id (expected_id).
- ///
- /// @param addr Address of the lease to be created
- /// @param lease_id Subnet-id of the lease to be created
- /// @param subnet_txt Text representation of the subnet, e.g. 192.0.2.0/24
- /// @param subnet_id Subnet-id of the subnet to be created
- /// @param sanity Sanity checks set in the configuration
- /// @param exp_lease_present whether the lease is expected to be inserted
- /// @param expected_id if exp_lease_present is true, lease's subnet id should
- /// match this value.
- /// @return lease from the backend (may return null)
- void
- leaseAddCheck6(string addr, SubnetID lease_id, string subnet_txt, SubnetID subnet_id,
- CfgConsistency::LeaseSanity sanity, bool exp_lease_present,
- SubnetID expected_id) {
-
- // Let's start a backend in proper universe.
- startLeaseBackend(true);
-
- // Now set the sanity checks to a proper value.
- ASSERT_NO_THROW(CfgMgr::instance().getCurrentCfg()->getConsistency()
- ->setLeaseSanityCheck(sanity));
-
- // Create the subnet and add it to configuration.
- if (!subnet_txt.empty()) {
- Subnet6Ptr subnet = createSubnet6(subnet_txt, subnet_id);
- ASSERT_NO_THROW(CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->add(subnet));
- }
-
- // Now try to insert the lease.
- Lease6Ptr l = newLease6(addr, lease_id);
- bool result = false;
- EXPECT_NO_THROW(result = LeaseMgrFactory::instance().addLease(l));
- EXPECT_EQ(result, exp_lease_present);
-
- Lease6Ptr from_backend;
- if (result && exp_lease_present) {
-
- ASSERT_TRUE(from_backend = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA, IOAddress(addr)));
-
- EXPECT_EQ(l->addr_, from_backend->addr_);
- EXPECT_EQ(from_backend->subnet_id_, expected_id);
- }
- }
};
// Verify whether configuration parser is able to understand the values
parserCheck(cfg, bogus4, true, CfgConsistency::LEASE_CHECK_NONE);
}
-// This test checks how the code behaves when there is:
-// no subnets configured, sanity-check is set to none
-// Expected behavior: lease added as is
-TEST_F(SanityChecksTest, memfileAdd4NoSubnetNone) {
- leaseAddCheck4("192.0.2.1", 1, "", 0, CfgConsistency::LEASE_CHECK_NONE,
- true, 1);
-}
-
-// This test checks how the code behaves when there is:
-// no subnets configured, sanity-check is set to warn
-// Expected behavior: lease added as is
-TEST_F(SanityChecksTest, memfileAdd4NoSubnetWarn) {
- leaseAddCheck4("192.0.2.1", 1, "", 0, CfgConsistency::LEASE_CHECK_WARN,
- true, 1);
-}
-
-// This test checks how the code behaves when there is:
-// no subnets configured, sanity-check is set to fix
-// Expected behavior: lease added as is
-TEST_F(SanityChecksTest, memfileAdd4NoSubnetFix) {
- leaseAddCheck4("192.0.2.1", 1, "", 0, CfgConsistency::LEASE_CHECK_FIX,
- true, 1);
-}
-
-// This test checks how the code behaves when there is:
-// no subnets configured, sanity-check is set to fix-del
-// Expected behavior: lease not added
-TEST_F(SanityChecksTest, memfileAdd4NoSubnetFixDel) {
- leaseAddCheck4("192.0.2.1", 1, "", 0, CfgConsistency::LEASE_CHECK_FIX_DEL,
- false, 1);
-}
-
-// This test checks how the code behaves when there is:
-// no subnets configured, sanity-check is set to del
-// Expected behavior: lease not added
-TEST_F(SanityChecksTest, memfileAdd4NoSubnetDel) {
- leaseAddCheck4("192.0.2.1", 1, "", 0, CfgConsistency::LEASE_CHECK_DEL,
- false, 1);
-}
-
-// This test checks how the code behaves when there is:
-// one subnet configured, sanity-check is set to none
-// Expected behavior: lease added as is
-TEST_F(SanityChecksTest, memfileAdd4checksNone) {
- leaseAddCheck4("192.0.2.1", 1, "192.0.2.0/24", 2,
- CfgConsistency::LEASE_CHECK_NONE, true, 1);
-}
-
-// This test checks how the code behaves when there is:
-// one subnet configured, sanity-check is set to warn
-// Expected behavior: lease added as is
-TEST_F(SanityChecksTest, memfileAdd4checksWarn) {
- leaseAddCheck4("192.0.2.1", 1, "192.0.2.0/24", 2,
- CfgConsistency::LEASE_CHECK_WARN, true, 1);
-}
-
-// This test checks how the code behaves when there is:
-// one subnet configured, sanity-check is set to fix
-// Expected behavior: lease added with corrected subnet-id
-TEST_F(SanityChecksTest, memfileAdd4checksFix) {
- leaseAddCheck4("192.0.2.1", 1, "192.0.2.0/24", 2,
- CfgConsistency::LEASE_CHECK_FIX, true, 2);
-}
-
-// This test checks how the code behaves when there is:
-// one subnet configured, sanity-check is set to fix-del
-// Expected behavior: lease added with corrected subnet-id
-TEST_F(SanityChecksTest, memfileAdd4checksFixdel1) {
- leaseAddCheck4("192.0.2.1", 1, "192.0.2.0/24", 2,
- CfgConsistency::LEASE_CHECK_FIX_DEL, true, 2);
-}
-
-// This test checks how the code behaves when there is:
-// one subnet configured (but the lease does not belong to it),
-// sanity-check is set to fix-del
-// Expected behavior: lease not added
-TEST_F(SanityChecksTest, memfileAdd4checksFixdel2) {
- leaseAddCheck4("192.0.2.1", 1, "192.0.3.0/24", 2,
- CfgConsistency::LEASE_CHECK_FIX_DEL, false, 0);
-}
-
-// This test checks how the code behaves when there is:
-// one subnet configured, sanity-check is set to del
-// Expected behavior: lease not added
-TEST_F(SanityChecksTest, memfileAdd4checksDel) {
- leaseAddCheck4("192.0.2.1", 1, "192.0.2.0/24", 2,
- CfgConsistency::LEASE_CHECK_DEL, false, 0);
-}
-
-// This test checks how the code behaves when there is:
-// no subnets configured, sanity-check is set to none
-// Expected behavior: lease added as is
-TEST_F(SanityChecksTest, memfileAdd6NoSubnetNone) {
- leaseAddCheck6("2001::1", 1, "", 0,
- CfgConsistency::LEASE_CHECK_NONE, true, 1);
-}
-
-// This test checks how the code behaves when there is:
-// no subnets configured, sanity-check is set to warn
-// Expected behavior: lease added as is
-TEST_F(SanityChecksTest, memfileAdd6NoSubnetWarn) {
- leaseAddCheck6("2000::1", 1, "", 0,
- CfgConsistency::LEASE_CHECK_WARN, true, 1);
-}
-
-// This test checks how the code behaves when there is:
-// no subnets configured, sanity-check is set to fix
-// Expected behavior: lease added as is
-TEST_F(SanityChecksTest, memfileAdd6NoSubnetFix) {
- leaseAddCheck6("2000::1", 1, "", 0,
- CfgConsistency::LEASE_CHECK_FIX, true, 1);
-}
-
-// This test checks how the code behaves when there is:
-// no subnets configured, sanity-check is set to fix-del
-// Expected behavior: lease not added
-TEST_F(SanityChecksTest, memfileAdd6NoSubnetFixDel) {
- leaseAddCheck6("2000::1", 1, "", 0,
- CfgConsistency::LEASE_CHECK_FIX_DEL, false, 1);
-}
-
-// This test checks how the code behaves when there is:
-// no subnets configured, sanity-check is set to del
-// Expected behavior: lease not added
-TEST_F(SanityChecksTest, memfileAdd6NoSubnetDel) {
- leaseAddCheck6("2000::1", 1, "", 0,
- CfgConsistency::LEASE_CHECK_DEL, false, 1);
-}
-
-// This test checks how the code behaves when there is:
-// one subnet configured, sanity-check is set to none
-// Expected behavior: lease added as is
-TEST_F(SanityChecksTest, memfileAdd6checksNone) {
- leaseAddCheck6("2000::1", 1, "2000::/16", 2,
- CfgConsistency::LEASE_CHECK_NONE, true, 1);
-}
-
-// This test checks how the code behaves when there is:
-// one subnet configured, sanity-check is set to warn
-// Expected behavior: lease added as is
-TEST_F(SanityChecksTest, memfileAdd6checksWarn) {
- leaseAddCheck6("2000::1", 1, "2000::/16", 2,
- CfgConsistency::LEASE_CHECK_WARN, true, 1);
-}
-
-// This test checks how the code behaves when there is:
-// one subnet configured, sanity-check is set to fix
-// Expected behavior: lease added with corrected subnet-id
-TEST_F(SanityChecksTest, memfileAdd6checksFix) {
- leaseAddCheck6("2000::1", 1, "2000::/16", 2,
- CfgConsistency::LEASE_CHECK_FIX, true, 2);
-}
-
-// This test checks how the code behaves when there is:
-// one subnet configured, sanity-check is set to fix-del
-// Expected behavior: lease added with corrected subnet-id
-TEST_F(SanityChecksTest, memfileAdd6checksFixdel1) {
- leaseAddCheck6("2000::1", 1, "2000::/16", 2,
- CfgConsistency::LEASE_CHECK_FIX_DEL, true, 2);
-}
-
-// This test checks how the code behaves when there is:
-// one subnet configured (but the lease does not belong to it),
-// sanity-check is set to fix-del
-// Expected behavior: lease not added
-TEST_F(SanityChecksTest, memfileAdd6checksFixdel2) {
- leaseAddCheck6("2000::1", 1, "3000::/16", 2,
- CfgConsistency::LEASE_CHECK_FIX_DEL, false, 0);
-}
-
-// This test checks how the code behaves when there is:
-// one subnet configured, sanity-check is set to del
-// Expected behavior: lease not added
-TEST_F(SanityChecksTest, memfileAdd6checksDel) {
- leaseAddCheck6("2000::1", 1, "2000::/16", 2,
- CfgConsistency::LEASE_CHECK_DEL, false, 0);
-}