summaryrefslogtreecommitdiffstats
path: root/src/lib/dhcpsrv/tests/sanity_checks_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/dhcpsrv/tests/sanity_checks_unittest.cc')
-rw-r--r--src/lib/dhcpsrv/tests/sanity_checks_unittest.cc378
1 files changed, 378 insertions, 0 deletions
diff --git a/src/lib/dhcpsrv/tests/sanity_checks_unittest.cc b/src/lib/dhcpsrv/tests/sanity_checks_unittest.cc
new file mode 100644
index 0000000..cb305d6
--- /dev/null
+++ b/src/lib/dhcpsrv/tests/sanity_checks_unittest.cc
@@ -0,0 +1,378 @@
+// Copyright (C) 2018-2020 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+#include <dhcpsrv/cfg_consistency.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/parsers/sanity_checks_parser.h>
+#include <dhcpsrv/srv_config.h>
+#include <dhcpsrv/lease.h>
+#include <dhcpsrv/lease_mgr_factory.h>
+#include <dhcpsrv/subnet.h>
+#include <dhcpsrv/sanity_checker.h>
+#include <dhcpsrv/testutils/test_utils.h>
+#include <util/range_utilities.h>
+#include <cc/data.h>
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc::data;
+using namespace isc::asiolink;
+using namespace isc::dhcp;
+using namespace isc::dhcp::test;
+
+class SanityChecksTest : public ::testing::Test {
+public:
+
+ SanityChecksTest() {
+ LeaseMgrFactory::destroy();
+ }
+
+ void startLeaseBackend(bool v6) {
+ std::ostringstream s;
+ s << "type=memfile " << (v6 ? "universe=6 " : "universe=4 ")
+ << "persist=false lfc-interval=0";
+ LeaseMgrFactory::create(s.str());
+ }
+
+ void setLeaseCheck(CfgConsistency::LeaseSanity sanity) {
+ CfgMgr::instance().getCurrentCfg()->getConsistency()->setLeaseSanityCheck(sanity);
+ }
+
+ ~SanityChecksTest() {
+ CfgMgr::instance().clear();
+ LeaseMgrFactory::destroy();
+ }
+
+ /// @brief Generates a simple IPv4 lease.
+ ///
+ /// The HW address is randomly generated, subnet_id is specified.
+ ///
+ /// @param address Lease address.
+ /// @param subnet_id ID of the subnet to use.
+ ///
+ /// @return new lease with random content
+ Lease4Ptr newLease4(const IOAddress& address, SubnetID subnet_id) {
+
+ // Randomize HW address.
+ vector<uint8_t> mac(6);
+ isc::util::fillRandom(mac.begin(), mac.end());
+ HWAddrPtr hwaddr(new HWAddr(mac, HTYPE_ETHER));
+
+ vector<uint8_t> clientid(1);
+
+ time_t timestamp = time(NULL) - 86400 + random()%86400;
+
+ // Return created lease.
+ return (Lease4Ptr(new Lease4(address, hwaddr,
+ &clientid[0], 0, // no client-id
+ 1200, // valid
+ timestamp, subnet_id, false, false, "")));
+ }
+
+ /// @brief Generates a simple IPv6 lease.
+ ///
+ /// The DUID and IAID are randomly generated, subnet_id is specified.
+ ///
+ /// @param address Lease address.
+ /// @param subnet_id ID of the subnet to use.
+ ///
+ /// @return new lease with random content
+ Lease6Ptr newLease6(const IOAddress& address, SubnetID subnet_id) {
+ // Let's generate DUID of random length.
+ std::vector<uint8_t> duid_vec(8 + random()%20);
+ // And then fill it with random value.
+ isc::util::fillRandom(duid_vec.begin(), duid_vec.end());
+ DuidPtr duid(new DUID(duid_vec));
+
+ Lease::Type lease_type = Lease::TYPE_NA;
+ uint32_t iaid = 1 + random()%100;
+
+ std::ostringstream hostname;
+ hostname << "hostname" << (random() % 2048);
+
+ // Return created lease.
+ Lease6Ptr lease(new Lease6(lease_type, address, duid, iaid,
+ 1000, 1200, // pref, valid
+ subnet_id,
+ false, false, "")); // fqdn fwd, rev, hostname
+ return (lease);
+ }
+
+ Subnet4Ptr createSubnet4(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)));
+ }
+
+ Subnet6Ptr createSubnet6(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)));
+ }
+
+ void
+ parserCheck(SrvConfig& cfg, const string& txt, bool exp_throw,
+ CfgConsistency::LeaseSanity exp_sanity) {
+
+ SanityChecksParser parser;
+
+ ElementPtr json;
+ EXPECT_NO_THROW(json = Element::fromJSON(txt));
+
+ if (exp_throw) {
+ EXPECT_THROW(parser.parse(cfg, json), DhcpConfigError);
+
+ return;
+ }
+
+ // Should not throw.
+ EXPECT_NO_THROW(parser.parse(cfg, json));
+
+ EXPECT_EQ(cfg.getConsistency()->getLeaseSanityCheck(), exp_sanity);
+ }
+
+};
+
+// Verify whether configuration parser is able to understand the values
+// that are valid and reject those that are not.
+TEST_F(SanityChecksTest, leaseCheck) {
+
+ // These are valid and should be accepted.
+ string valid1 = "{ \"lease-checks\": \"none\" }";
+ string valid2 = "{ \"lease-checks\": \"warn\" }";
+ string valid3 = "{ \"lease-checks\": \"fix\" }";
+ string valid4 = "{ \"lease-checks\": \"fix-del\" }";
+ string valid5 = "{ \"lease-checks\": \"del\" }";
+
+ // These are not valid values or types.
+ string bogus1 = "{ \"lease-checks\": \"sanitize\" }";
+ string bogus2 = "{ \"lease-checks\": \"ignore\" }";
+ string bogus3 = "{ \"lease-checks\": true }";
+ string bogus4 = "{ \"lease-checks\": 42 }";
+
+ SrvConfig cfg;
+
+ // The default should be to none.
+ EXPECT_EQ(cfg.getConsistency()->getLeaseSanityCheck(),
+ CfgConsistency::LEASE_CHECK_NONE);
+
+ parserCheck(cfg, valid1, false, CfgConsistency::LEASE_CHECK_NONE);
+ parserCheck(cfg, valid2, false, CfgConsistency::LEASE_CHECK_WARN);
+ parserCheck(cfg, valid3, false, CfgConsistency::LEASE_CHECK_FIX);
+ parserCheck(cfg, valid4, false, CfgConsistency::LEASE_CHECK_FIX_DEL);
+ parserCheck(cfg, valid5, false, CfgConsistency::LEASE_CHECK_DEL);
+
+ parserCheck(cfg, bogus1, true, CfgConsistency::LEASE_CHECK_NONE);
+ parserCheck(cfg, bogus2, true, CfgConsistency::LEASE_CHECK_NONE);
+ parserCheck(cfg, bogus3, true, CfgConsistency::LEASE_CHECK_NONE);
+ parserCheck(cfg, bogus4, true, CfgConsistency::LEASE_CHECK_NONE);
+}
+
+// Verify whether sanity checker works as expected (valid v4).
+TEST_F(SanityChecksTest, valid4) {
+ // Create network and lease.
+ CfgMgr::instance().setFamily(AF_INET);
+ Subnet4Ptr subnet = createSubnet4("192.168.1.0/24", 1);
+ CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->add(subnet);
+ IOAddress addr("192.168.1.1");
+ Lease4Ptr lease = newLease4(addr, 1);
+
+ // Check the lease.
+ setLeaseCheck(CfgConsistency::LEASE_CHECK_FIX_DEL);
+ SanityChecker checker;
+ checker.checkLease(lease);
+
+ // Verify the lease is still here in the same subnet.
+ ASSERT_TRUE(lease);
+ EXPECT_EQ(subnet->getID(), lease->subnet_id_);
+}
+
+// Verify whether sanity checker works as expected (valid v6).
+TEST_F(SanityChecksTest, valid6) {
+ // Create network and lease.
+ CfgMgr::instance().setFamily(AF_INET6);
+ Subnet6Ptr subnet = createSubnet6("2001:db8:1::/64", 1);
+ CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->add(subnet);
+ IOAddress addr("2001:db8:1::1");
+ Lease6Ptr lease = newLease6(addr, 1);
+
+ // Check the lease.
+ setLeaseCheck(CfgConsistency::LEASE_CHECK_FIX_DEL);
+ SanityChecker checker;
+ checker.checkLease(lease);
+
+ // Verify the lease is still here in the same subnet.
+ ASSERT_TRUE(lease);
+ EXPECT_EQ(subnet->getID(), lease->subnet_id_);
+}
+
+// Verify whether sanity checker works as expected (wrong subnet v4).
+TEST_F(SanityChecksTest, wrongSubnet4) {
+ // Create networks and lease in the second and wrong subnet.
+ CfgMgr::instance().setFamily(AF_INET);
+ Subnet4Ptr subnet1 = createSubnet4("192.168.1.0/24", 1);
+ Subnet4Ptr subnet2 = createSubnet4("192.168.2.0/24", 2);
+ CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->add(subnet1);
+ CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->add(subnet2);
+ IOAddress addr("192.168.1.1");
+ Lease4Ptr lease = newLease4(addr, 2);
+
+ // Check the lease.
+ setLeaseCheck(CfgConsistency::LEASE_CHECK_FIX_DEL);
+ SanityChecker checker;
+ checker.checkLease(lease);
+
+ // Verify the lease is still here but was moved to the first and right subnet.
+ ASSERT_TRUE(lease);
+ EXPECT_EQ(subnet1->getID(), lease->subnet_id_);
+}
+
+// Verify whether sanity checker works as expected (wrong subnet v6).
+TEST_F(SanityChecksTest, wrongSubnet6) {
+ // Create networks and lease in the second and wrong subnet.
+ CfgMgr::instance().setFamily(AF_INET6);
+ Subnet6Ptr subnet1 = createSubnet6("2001:db8:1::/64", 1);
+ Subnet6Ptr subnet2 = createSubnet6("2001:db8:2::/64", 2);
+ CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->add(subnet1);
+ CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->add(subnet2);
+ IOAddress addr("2001:db8:1::1");
+ Lease6Ptr lease = newLease6(addr, 2);
+
+ // Check the lease.
+ setLeaseCheck(CfgConsistency::LEASE_CHECK_FIX_DEL);
+ SanityChecker checker;
+ checker.checkLease(lease);
+
+ // Verify the lease is still here but was moved to the first and right subnet.
+ ASSERT_TRUE(lease);
+ EXPECT_EQ(subnet1->getID(), lease->subnet_id_);
+}
+
+// Verify whether sanity checker works as expected (no subnet v4).
+TEST_F(SanityChecksTest, noSubnet4) {
+ // Create network and lease in a wrong subnet.
+ CfgMgr::instance().setFamily(AF_INET);
+ Subnet4Ptr subnet = createSubnet4("192.168.2.0/24", 1);
+ CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->add(subnet);
+ IOAddress addr("192.168.1.1");
+ Lease4Ptr lease = newLease4(addr, 1);
+
+ // Check the lease.
+ setLeaseCheck(CfgConsistency::LEASE_CHECK_FIX_DEL);
+ SanityChecker checker;
+ checker.checkLease(lease);
+
+ // Verify the lease was removed because its subnet does not exist,
+ EXPECT_FALSE(lease);
+}
+
+// Verify whether sanity checker works as expected (no subnet v6).
+TEST_F(SanityChecksTest, noSubnet6) {
+ // Create network and lease in a wrong subnet.
+ CfgMgr::instance().setFamily(AF_INET6);
+ Subnet6Ptr subnet = createSubnet6("2001:db8:2::/64", 1);
+ CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->add(subnet);
+ IOAddress addr("2001:db8:1::1");
+ Lease6Ptr lease = newLease6(addr, 1);
+
+ // Check the lease.
+ setLeaseCheck(CfgConsistency::LEASE_CHECK_FIX_DEL);
+ SanityChecker checker;
+ checker.checkLease(lease);
+
+ // Verify the lease was removed because its subnet does not exist,
+ EXPECT_FALSE(lease);
+}
+
+// Verify whether sanity checker works as expected (guard v4).
+TEST_F(SanityChecksTest, guard4) {
+ // Create networks and lease in the first and guarded subnet.
+ CfgMgr::instance().setFamily(AF_INET);
+ Subnet4Ptr subnet1 = createSubnet4("192.168.1.0/24", 1);
+ subnet1->allowClientClass("foo");
+ Subnet4Ptr subnet2 = createSubnet4("192.168.1.100/24", 2);
+ CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->add(subnet1);
+ CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->add(subnet2);
+ IOAddress addr("192.168.1.1");
+ Lease4Ptr lease = newLease4(addr, 1);
+
+ // Check the lease.
+ setLeaseCheck(CfgConsistency::LEASE_CHECK_FIX_DEL);
+ SanityChecker checker;
+ checker.checkLease(lease);
+
+ // Verify the lease is still here and in the guarded subnet.
+ ASSERT_TRUE(lease);
+ EXPECT_EQ(subnet1->getID(), lease->subnet_id_);
+}
+
+// Verify whether sanity checker works as expected (guard v6).
+TEST_F(SanityChecksTest, guard6) {
+ // Create networks and lease in the first and guarded subnet.
+ CfgMgr::instance().setFamily(AF_INET6);
+ Subnet6Ptr subnet1 = createSubnet6("2001:db8:1::/64", 1);
+ subnet1->allowClientClass("foo");
+ Subnet6Ptr subnet2 = createSubnet6("2001:db8:2::100/64", 2);
+ CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->add(subnet1);
+ CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->add(subnet2);
+ IOAddress addr("2001:db8:1::1");
+ Lease6Ptr lease = newLease6(addr, 1);
+
+ // Check the lease.
+ setLeaseCheck(CfgConsistency::LEASE_CHECK_FIX_DEL);
+ SanityChecker checker;
+ checker.checkLease(lease);
+
+ // Verify the lease is still here and in the guarded subnet.
+ ASSERT_TRUE(lease);
+ EXPECT_EQ(subnet1->getID(), lease->subnet_id_);
+}
+
+// Verify whether sanity checker works as expected (guard only v4).
+TEST_F(SanityChecksTest, guardOnly4) {
+ // Create guarded network and lease.
+ CfgMgr::instance().setFamily(AF_INET);
+ Subnet4Ptr subnet = createSubnet4("192.168.1.0/24", 1);
+ subnet->allowClientClass("foo");
+ CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->add(subnet);
+ IOAddress addr("192.168.1.1");
+ Lease4Ptr lease = newLease4(addr, 1);
+
+ // Check the lease.
+ setLeaseCheck(CfgConsistency::LEASE_CHECK_FIX_DEL);
+ SanityChecker checker;
+ checker.checkLease(lease);
+
+ // Verify the lease is still here in the same subnet.
+ ASSERT_TRUE(lease);
+ EXPECT_EQ(subnet->getID(), lease->subnet_id_);
+}
+
+// Verify whether sanity checker works as expected (valid v6).
+TEST_F(SanityChecksTest, guardOnly6) {
+ // Create guarded network and lease.
+ CfgMgr::instance().setFamily(AF_INET6);
+ Subnet6Ptr subnet = createSubnet6("2001:db8:1::/64", 1);
+ subnet->allowClientClass("foo");
+ CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->add(subnet);
+ IOAddress addr("2001:db8:1::1");
+ Lease6Ptr lease = newLease6(addr, 1);
+
+ // Check the lease.
+ setLeaseCheck(CfgConsistency::LEASE_CHECK_FIX_DEL);
+ SanityChecker checker;
+ checker.checkLease(lease);
+
+ // Verify the lease is still here in the same subnet.
+ ASSERT_TRUE(lease);
+ EXPECT_EQ(subnet->getID(), lease->subnet_id_);
+}
+