From: Tomek Mrugalski Date: Fri, 28 Sep 2012 18:40:45 +0000 (+0200) Subject: [2269] Tests for dhcp6 config parser implemented. X-Git-Tag: trac2402_base~17^2~10^2~7 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ff837bb1fc512920556a796c40cacc20407b165c;p=thirdparty%2Fkea.git [2269] Tests for dhcp6 config parser implemented. --- diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc index 7c21941d4d..f9457d5b77 100644 --- a/src/bin/dhcp6/dhcp6_srv.cc +++ b/src/bin/dhcp6/dhcp6_srv.cc @@ -42,6 +42,13 @@ const uint32_t HARDCODED_VALID_LIFETIME = 7200; // in seconds const std::string HARDCODED_DNS_SERVER = "2001:db8:1::1"; Dhcpv6Srv::Dhcpv6Srv(uint16_t port) { + if (port == 0) { + // used for testing purposes. Some tests, e.g. configuration parser, + // require Dhcpv6Srv object, but they don't really need it to do + // anything. This speed up and simplifies the tests. + return; + } + LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_OPEN_SOCKET).arg(port); // First call to instance() will create IfaceMgr (it's a singleton) diff --git a/src/bin/dhcp6/tests/Makefile.am b/src/bin/dhcp6/tests/Makefile.am index 30d2c384cd..14797335b3 100644 --- a/src/bin/dhcp6/tests/Makefile.am +++ b/src/bin/dhcp6/tests/Makefile.am @@ -45,6 +45,7 @@ TESTS += dhcp6_unittests dhcp6_unittests_SOURCES = dhcp6_unittests.cc dhcp6_unittests_SOURCES += dhcp6_srv_unittest.cc dhcp6_unittests_SOURCES += ctrl_dhcp6_srv_unittest.cc +dhcp6_unittests_SOURCES += config_parser_unittest.cc dhcp6_unittests_SOURCES += ../dhcp6_srv.h ../dhcp6_srv.cc dhcp6_unittests_SOURCES += ../dhcp6_log.h ../dhcp6_log.cc dhcp6_unittests_SOURCES += ../ctrl_dhcp6_srv.cc diff --git a/src/bin/dhcp6/tests/config_parser_unittest.cc b/src/bin/dhcp6/tests/config_parser_unittest.cc new file mode 100644 index 0000000000..8c38762bdd --- /dev/null +++ b/src/bin/dhcp6/tests/config_parser_unittest.cc @@ -0,0 +1,225 @@ +// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +using namespace std; +using namespace isc; +using namespace isc::dhcp; +using namespace isc::asiolink; +using namespace isc::data; +using namespace isc::config; + +namespace { + +class Dhcp6ParserTest : public ::testing::Test { +public: + Dhcp6ParserTest() + :rcode_(-1) { + // open port 0 means to not do anything at all + srv_ = new Dhcpv6Srv(0); + } + + ~Dhcp6ParserTest() { + delete srv_; + }; + + Dhcpv6Srv * srv_; + + int rcode_; + ConstElementPtr comment_; +}; + + +TEST_F(Dhcp6ParserTest, version) { + + ConstElementPtr x; + + EXPECT_NO_THROW(x = configureDhcp6Server(*srv_, + Element::fromJSON("{\"version\": 0}"))); + + // returned value must be 0 (configuration accepted) + ASSERT_TRUE(x); + comment_ = parseAnswer(rcode_, x); + EXPECT_EQ(0, rcode_); +} + +TEST_F(Dhcp6ParserTest, bogus_command) { + + ConstElementPtr x; + + EXPECT_NO_THROW(x = configureDhcp6Server(*srv_, + Element::fromJSON("{\"bogus\": 5}"))); + + // returned value must be 1 (configuration parse error) + ASSERT_TRUE(x); + comment_ = parseAnswer(rcode_, x); + EXPECT_EQ(1, rcode_); +} + +TEST_F(Dhcp6ParserTest, empty_subnet) { + + ConstElementPtr x; + + EXPECT_NO_THROW(x = configureDhcp6Server(*srv_, + Element::fromJSON("{ \"interface\": [ \"all\" ]," + "\"preferred-lifetime\": 3000," + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"subnet6\": [ ], " + "\"valid-lifetime\": 4000 }"))); + + // returned value must be 1 (configuration parse error) + ASSERT_TRUE(x); + comment_ = parseAnswer(rcode_, x); + EXPECT_EQ(0, rcode_); +} + +TEST_F(Dhcp6ParserTest, subnet_global_defaults) { + + ConstElementPtr x; + + string config = "{ \"interface\": [ \"all\" ]," + "\"preferred-lifetime\": 3000," + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"subnet6\": [ { " + " \"pool\": [ \"2001:db8:1::1 - 2001:db8:1::ffff\" ]," + " \"subnet\": \"2001:db8:1::/64\" } ]," + "\"valid-lifetime\": 4000 }"; + cout << config << endl; + + ElementPtr json = Element::fromJSON(config); + + EXPECT_NO_THROW(x = configureDhcp6Server(*srv_, json)); + + // returned value must be 1 (configuration parse error) + ASSERT_TRUE(x); + comment_ = parseAnswer(rcode_, x); + EXPECT_EQ(0, rcode_); + + Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5")); + ASSERT_TRUE(subnet); + EXPECT_EQ(1000, subnet->getT1()); + EXPECT_EQ(2000, subnet->getT2()); + EXPECT_EQ(3000, subnet->getPreferred()); + EXPECT_EQ(4000, subnet->getValid()); +} + +// +TEST_F(Dhcp6ParserTest, subnet_local) { + + ConstElementPtr x; + + string config = "{ \"interface\": [ \"all\" ]," + "\"preferred-lifetime\": 3000," + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"subnet6\": [ { " + " \"pool\": [ \"2001:db8:1::1 - 2001:db8:1::ffff\" ]," + " \"renew-timer\": 1, " + " \"rebind-timer\": 2, " + " \"preferred-lifetime\": 3," + " \"valid-lifetime\": 4," + " \"subnet\": \"2001:db8:1::/64\" } ]," + "\"valid-lifetime\": 4000 }"; + cout << config << endl; + + ElementPtr json = Element::fromJSON(config); + + EXPECT_NO_THROW(x = configureDhcp6Server(*srv_, json)); + + // returned value must be 1 (configuration parse error) + ASSERT_TRUE(x); + comment_ = parseAnswer(rcode_, x); + EXPECT_EQ(0, rcode_); + + Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5")); + ASSERT_TRUE(subnet); + EXPECT_EQ(1, subnet->getT1()); + EXPECT_EQ(2, subnet->getT2()); + EXPECT_EQ(3, subnet->getPreferred()); + EXPECT_EQ(4, subnet->getValid()); +} + +TEST_F(Dhcp6ParserTest, pool_out_of_subnet) { + + ConstElementPtr x; + + string config = "{ \"interface\": [ \"all\" ]," + "\"preferred-lifetime\": 3000," + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"subnet6\": [ { " + " \"pool\": [ \"4001:db8:1::/80\" ]," + " \"subnet\": \"2001:db8:1::/64\" } ]," + "\"valid-lifetime\": 4000 }"; + cout << config << endl; + + ElementPtr json = Element::fromJSON(config); + + EXPECT_NO_THROW(x = configureDhcp6Server(*srv_, json)); + + // returned value must be 2 (values error) + // as the pool does not belong to that subnet + ASSERT_TRUE(x); + comment_ = parseAnswer(rcode_, x); + EXPECT_EQ(2, rcode_); + +} + +TEST_F(Dhcp6ParserTest, subnet_prefix_len) { + + ConstElementPtr x; + + string config = "{ \"interface\": [ \"all\" ]," + "\"preferred-lifetime\": 3000," + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"subnet6\": [ { " + " \"pool\": [ \"2001:db8:1::/80\" ]," + " \"subnet\": \"2001:db8:1::/64\" } ]," + "\"valid-lifetime\": 4000 }"; + cout << config << endl; + + ElementPtr json = Element::fromJSON(config); + + EXPECT_NO_THROW(x = configureDhcp6Server(*srv_, json)); + + // returned value must be 1 (configuration parse error) + ASSERT_TRUE(x); + comment_ = parseAnswer(rcode_, x); + EXPECT_EQ(0, rcode_); + + Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5")); + ASSERT_TRUE(subnet); + EXPECT_EQ(1000, subnet->getT1()); + EXPECT_EQ(2000, subnet->getT2()); + EXPECT_EQ(3000, subnet->getPreferred()); + EXPECT_EQ(4000, subnet->getValid()); +} + +}; diff --git a/src/lib/dhcp/cfgmgr.h b/src/lib/dhcp/cfgmgr.h index 5b73f2b966..f57ef994d6 100644 --- a/src/lib/dhcp/cfgmgr.h +++ b/src/lib/dhcp/cfgmgr.h @@ -98,6 +98,21 @@ public: /// to remove subnets. The only case where subnet6 removal would be /// needed is a dynamic server reconfiguration - a use case that is not /// planned to be supported any time soon. + + /// @brief removes all subnets + /// + /// This method removes all existing subnets. It is used during + /// reconfiguration - old configuration is wiped and new definitions + /// are used to recreate subnets. + /// + /// @todo Implement more intelligent approach. Note that comparison + /// between old and new configuration is tricky. For example: is + /// 2000::/64 and 2000::/48 the same subnet or is it something + /// completely new? + void deleteSubnets6() { + subnets6_.clear(); + } + protected: /// @brief Protected constructor. diff --git a/src/lib/dhcp/tests/cfgmgr_unittest.cc b/src/lib/dhcp/tests/cfgmgr_unittest.cc index 7f287eaf80..bdd9a37541 100644 --- a/src/lib/dhcp/tests/cfgmgr_unittest.cc +++ b/src/lib/dhcp/tests/cfgmgr_unittest.cc @@ -266,6 +266,9 @@ TEST(Subnet6Test, Subnet6_Pool6_checks) { Pool6Ptr pool3(new Pool6(Pool6::TYPE_IA, IOAddress("3000::"), 16)); EXPECT_THROW(subnet->addPool6(pool3), BadValue); + + Pool6Ptr pool4(new Pool6(Pool6::TYPE_IA, IOAddress("4001:db8:1::"), 80)); + EXPECT_THROW(subnet->addPool6(pool4), BadValue); } // This test verifies if the configuration manager is able to hold and return @@ -294,6 +297,10 @@ TEST(CfgMgrTest, subnet6) { EXPECT_EQ(subnet2, cfg_mgr.getSubnet6(IOAddress("3000::dead:beef"))); EXPECT_EQ(Subnet6Ptr(), cfg_mgr.getSubnet6(IOAddress("5000::1"))); + cfg_mgr.deleteSubnets6(); + EXPECT_EQ(Subnet6Ptr(), cfg_mgr.getSubnet6(IOAddress("200::123"))); + EXPECT_EQ(Subnet6Ptr(), cfg_mgr.getSubnet6(IOAddress("3000::123"))); + EXPECT_EQ(Subnet6Ptr(), cfg_mgr.getSubnet6(IOAddress("4000::123"))); } } // end of anonymous namespace