From: Stephen Morris Date: Sun, 11 Nov 2012 22:55:41 +0000 (+0000) Subject: [2342] Merge branch 'master' into trac2342 X-Git-Tag: trac2487_base~1^2~31 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4e7a8f7575704052e70432f1e2508d310e8f6bb0;p=thirdparty%2Fkea.git [2342] Merge branch 'master' into trac2342 Conflicts: doc/devel/mainpage.dox src/lib/dhcp/Makefile.am src/lib/dhcp/lease_mgr.cc src/lib/dhcp/lease_mgr.h src/lib/dhcp/tests/Makefile.am src/lib/dhcp/tests/lease_mgr_unittest.cc A number of other files were modified to resolve incompatibilities in the way lease managers were created, and to promote the dummy "memfile" lease manager to a fully-fledged lease manager with unit tests. --- 4e7a8f7575704052e70432f1e2508d310e8f6bb0 diff --cc doc/devel/mainpage.dox index a36d2270aa,e1761ec74a..db42c147db --- a/doc/devel/mainpage.dox +++ b/doc/devel/mainpage.dox @@@ -29,7 -29,10 +29,11 @@@ * - @subpage libdhcp * - @subpage libdhcpIntro * - @subpage libdhcpIfaceMgr + * - @subpage libdhcpsrv + * - @subpage leasemgr + * - @subpage cfgmgr + * - @subpage allocengine + * - @subpage dhcp-database-backends * - @subpage perfdhcpInternals * * @section misc Miscellaneous topics diff --cc src/bin/dhcp6/dhcp6_srv.cc index 7c57a6139c,7b1f721aa4..5f844a3d94 --- a/src/bin/dhcp6/dhcp6_srv.cc +++ b/src/bin/dhcp6/dhcp6_srv.cc @@@ -12,6 -12,6 +12,8 @@@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. ++#include ++ #include #include @@@ -27,6 -31,16 +33,17 @@@ #include #include #include + #include + #include ++#include + #include + #include + + // @todo: Replace this with MySQL_LeaseMgr (or a LeaseMgr factory) + // once it is merged + #include + + #include using namespace isc; using namespace isc::asiolink; @@@ -73,11 -85,22 +88,25 @@@ Dhcpv6Srv::Dhcpv6Srv(uint16_t port) : a return; } - shutdown_ = false; + // Instantiate LeaseMgr + // @todo: Replace this with MySQL_LeaseMgr (or a LeaseMgr factory) + // once it is merged - new isc::dhcp::test::Memfile_LeaseMgr(""); - ++#ifdef HAVE_MYSQL ++ LeaseMgrFactory::create("type=mysql user=kea password=kea name=kea host=localhost"); ++#else ++ LeaseMgrFactory::create("type=memfile"); ++#endif + LOG_INFO(dhcp6_logger, DHCP6_DB_BACKEND_STARTED) - .arg(LeaseMgr::instance().getName()); ++ .arg(LeaseMgrFactory::instance().getName()); + + // Instantiate allocation engine + alloc_engine_.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)); } Dhcpv6Srv::~Dhcpv6Srv() { IfaceMgr::instance().closeSockets(); + - LeaseMgr::destroy_instance(); ++ LeaseMgrFactory::destroy(); } void Dhcpv6Srv::shutdown() { diff --cc src/bin/dhcp6/tests/dhcp6_srv_unittest.cc index 20287066da,f1cf7de098..afb05d188b --- a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc +++ b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc @@@ -13,15 -13,26 +13,27 @@@ // PERFORMANCE OF THIS SOFTWARE. #include - #include + #include + #include #include - #include #include + #include + #include + #include + #include #include + #include + #include ++#include + #include + #include #include + #include + #include + #include #include #include #include @@@ -54,12 -63,144 +64,144 @@@ public class Dhcpv6SrvTest : public ::testing::Test { public: // these are empty for now, but let's keep them around - Dhcpv6SrvTest() { + Dhcpv6SrvTest() : rcode_(-1) { + subnet_ = Subnet6Ptr(new Subnet6(IOAddress("2001:db8:1::"), 48, 1000, + 2000, 3000, 4000)); + pool_ = Pool6Ptr(new Pool6(Pool6::TYPE_IA, IOAddress("2001:db8:1:1::"), 64)); + subnet_->addPool6(pool_); + + CfgMgr::instance().addSubnet6(subnet_); + } + + // Generate IA_NA option with specified parameters + boost::shared_ptr generateIA(uint32_t iaid, uint32_t t1, uint32_t t2) { + boost::shared_ptr ia = + boost::shared_ptr(new Option6IA(D6O_IA_NA, iaid)); + ia->setT1(t1); + ia->setT2(t2); + return (ia); + } + + // Generate client-id option + OptionPtr generateClientId(size_t duid_size = 32) { + + OptionBuffer clnt_duid(duid_size); + for (int i = 0; i < duid_size; i++) { + clnt_duid[i] = 100 + i; + } + + duid_ = DuidPtr(new DUID(clnt_duid)); + + return (OptionPtr(new Option(Option::V6, D6O_CLIENTID, + clnt_duid.begin(), + clnt_duid.begin() + duid_size))); + } + + // Checks if server response (ADVERTISE or REPLY) includes proper server-id. + void checkServerId(const Pkt6Ptr& rsp, const OptionPtr& expected_srvid) { + // check that server included its server-id + OptionPtr tmp = rsp->getOption(D6O_SERVERID); + EXPECT_EQ(tmp->getType(), expected_srvid->getType() ); + ASSERT_EQ(tmp->len(), expected_srvid->len() ); + EXPECT_TRUE(tmp->getData() == expected_srvid->getData()); + } + + // Checks if server response (ADVERTISE or REPLY) includes proper client-id. + void checkClientId(const Pkt6Ptr& rsp, const OptionPtr& expected_clientid) { + // check that server included our own client-id + OptionPtr tmp = rsp->getOption(D6O_CLIENTID); + ASSERT_TRUE(tmp); + EXPECT_EQ(expected_clientid->getType(), tmp->getType()); + ASSERT_EQ(expected_clientid->len(), tmp->len()); + + // check that returned client-id is valid + EXPECT_TRUE(expected_clientid->getData() == tmp->getData()); + } + + // Checks that server response (ADVERTISE or REPLY) contains proper IA_NA option + // It returns IAADDR option for each chaining with checkIAAddr method. + boost::shared_ptr checkIA_NA(const Pkt6Ptr& rsp, uint32_t expected_iaid, + uint32_t expected_t1, uint32_t expected_t2) { + OptionPtr tmp = rsp->getOption(D6O_IA_NA); + // Can't use ASSERT_TRUE() in method that returns something + if (!tmp) { + ADD_FAILURE() << "IA_NA option not present in response"; + return (boost::shared_ptr()); + } + + boost::shared_ptr ia = boost::dynamic_pointer_cast(tmp); + EXPECT_EQ(expected_iaid, ia->getIAID() ); + EXPECT_EQ(expected_t1, ia->getT1()); + EXPECT_EQ(expected_t2, ia->getT2()); + + tmp = ia->getOption(D6O_IAADDR); + boost::shared_ptr addr = boost::dynamic_pointer_cast(tmp); + return (addr); + } + + // Check that generated IAADDR option contains expected address. + void checkIAAddr(const boost::shared_ptr& addr, + const IOAddress& expected_addr, + uint32_t expected_preferred, uint32_t expected_valid) { + + // Check that the assigned address is indeed from the configured pool. + // Note that when comparing addresses, we compare the textual + // representation. IOAddress does not support being streamed to + // an ostream, which means it can't be used in EXPECT_EQ. + EXPECT_TRUE(subnet_->inPool(addr->getAddress())); + EXPECT_EQ(expected_addr.toText(), addr->getAddress().toText()); + EXPECT_EQ(addr->getPreferred(), subnet_->getPreferred()); + EXPECT_EQ(addr->getValid(), subnet_->getValid()); + } + + // Basic checks for generated response (message type and transaction-id). + void checkResponse(const Pkt6Ptr& rsp, uint8_t expected_message_type, + uint32_t expected_transid) { + ASSERT_TRUE(rsp); + EXPECT_EQ(expected_message_type, rsp->getType()); + EXPECT_EQ(expected_transid, rsp->getTransid()); + } + + // Checks if the lease sent to client is present in the database + Lease6Ptr checkLease(const DuidPtr& duid, const OptionPtr& ia_na, + boost::shared_ptr addr) { + boost::shared_ptr ia = boost::dynamic_pointer_cast(ia_na); + - Lease6Ptr lease = LeaseMgr::instance().getLease6(addr->getAddress()); ++ Lease6Ptr lease = LeaseMgrFactory::instance().getLease6(addr->getAddress()); + if (!lease) { + cout << "Lease for " << addr->getAddress().toText() + << " not found in the database backend."; + return (Lease6Ptr()); + } + + EXPECT_EQ(addr->getAddress().toText(), lease->addr_.toText()); + EXPECT_TRUE(*lease->duid_ == *duid); + EXPECT_EQ(ia->getIAID(), lease->iaid_); + EXPECT_EQ(subnet_->getID(), lease->subnet_id_); + + return (lease); } + ~Dhcpv6SrvTest() { + CfgMgr::instance().deleteSubnets6(); }; + + // A subnet used in most tests + Subnet6Ptr subnet_; + + // A pool used in most tests + Pool6Ptr pool_; + + // A DUID used in most tests (typically as client-id) + DuidPtr duid_; + + int rcode_; + ConstElementPtr comment_; }; + // Test verifies that the Dhcpv6_srv class can be instantiated. It checks a mode + // without open sockets and with sockets opened on a high port (to not require + // root privileges). TEST_F(Dhcpv6SrvTest, basic) { // srv has stubbed interface detection. It will read // interfaces.txt instead. It will pretend to have detected @@@ -150,79 -295,460 +296,460 @@@ TEST_F(Dhcpv6SrvTest, DUID) } } - TEST_F(Dhcpv6SrvTest, Solicit_basic) { + // This test checks if Option Request Option (ORO) is parsed correctly + // and the requested options are actually assigned. + TEST_F(Dhcpv6SrvTest, advertiseOptions) { + ConstElementPtr x; + string config = "{ \"interface\": [ \"all\" ]," + "\"preferred-lifetime\": 3000," + "\"rebind-timer\": 2000, " + "\"renew-timer\": 1000, " + "\"subnet6\": [ { " + " \"pool\": [ \"2001:db8:1::/64\" ]," + " \"subnet\": \"2001:db8:1::/48\", " + " \"option-data\": [ {" + " \"name\": \"OPTION_DNS_SERVERS\"," + " \"code\": 23," + " \"data\": \"2001 0DB8 1234 FFFF 0000 0000 0000 0001" + "2001 0DB8 1234 FFFF 0000 0000 0000 0002\"" + " }," + " {" + " \"name\": \"OPTION_FOO\"," + " \"code\": 1000," + " \"data\": \"1234\"" + " } ]" + " } ]," + "\"valid-lifetime\": 4000 }"; + + ElementPtr json = Element::fromJSON(config); + boost::scoped_ptr srv; - ASSERT_NO_THROW( srv.reset(new NakedDhcpv6Srv()) ); + ASSERT_NO_THROW(srv.reset(new NakedDhcpv6Srv(0))); - // a dummy content for client-id - OptionBuffer clntDuid(32); - for (int i = 0; i < 32; i++) { - clntDuid[i] = 100 + i; - } + EXPECT_NO_THROW(x = configureDhcp6Server(*srv, json)); + ASSERT_TRUE(x); + comment_ = parseAnswer(rcode_, x); + + ASSERT_EQ(0, rcode_); Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234)); + sol->setRemoteAddr(IOAddress("fe80::abcd")); + sol->addOption(generateIA(234, 1500, 3000)); + OptionPtr clientid = generateClientId(); + sol->addOption(clientid); + + // Pass it to the server and get an advertise + boost::shared_ptr adv = srv->processSolicit(sol); + + // check if we get response at all + ASSERT_TRUE(adv); + + // We have not requested option with code 1000 so it should not + // be included in the response. + ASSERT_FALSE(adv->getOption(1000)); + ASSERT_FALSE(adv->getOption(D6O_NAME_SERVERS)); + + // Let's now request option with code 1000. + // We expect that server will include this option in its reply. + boost::shared_ptr > + option_oro(new Option6IntArray(D6O_ORO)); + // Create vector with two option codes. + std::vector codes(2); + codes[0] = 1000; + codes[1] = D6O_NAME_SERVERS; + // Pass this code to option. + option_oro->setValues(codes); + // Append ORO to SOLICIT message. + sol->addOption(option_oro); + + // Need to process SOLICIT again after requesting new option. + adv = srv->processSolicit(sol); + ASSERT_TRUE(adv); + + OptionPtr tmp = adv->getOption(D6O_NAME_SERVERS); + ASSERT_TRUE(tmp); + + boost::shared_ptr reply_nameservers = + boost::dynamic_pointer_cast(tmp); + ASSERT_TRUE(reply_nameservers); + + Option6AddrLst::AddressContainer addrs = reply_nameservers->getAddresses(); + ASSERT_EQ(2, addrs.size()); + EXPECT_TRUE(addrs[0] == IOAddress("2001:db8:1234:FFFF::1")); + EXPECT_TRUE(addrs[1] == IOAddress("2001:db8:1234:FFFF::2")); + + // There is a dummy option with code 1000 we requested from a server. + // Expect that this option is in server's response. + tmp = adv->getOption(1000); + ASSERT_TRUE(tmp); + + // Check that the option contains valid data (from configuration). + std::vector data = tmp->getData(); + ASSERT_EQ(2, data.size()); + + const uint8_t foo_expected[] = { + 0x12, 0x34 + }; + EXPECT_EQ(0, memcmp(&data[0], foo_expected, 2)); + + // more checks to be implemented + } + + + // There are no dedicated tests for Dhcpv6Srv::handleIA_NA and Dhcpv6Srv::assignLeases + // as they are indirectly tested in Solicit and Request tests. + + // This test verifies that incoming SOLICIT can be handled properly, that an + // ADVERTISE is generated, that the response has an address and that address + // really belongs to the configured pool. + // + // This test sends a SOLICIT without any hint in IA_NA. + // + // constructed very simple SOLICIT message with: + // - client-id option (mandatory) + // - IA option (a request for address, without any addresses) + // + // expected returned ADVERTISE message: + // - copy of client-id + // - server-id + // - IA that includes IAADDR + TEST_F(Dhcpv6SrvTest, SolicitBasic) { + boost::scoped_ptr srv; + ASSERT_NO_THROW( srv.reset(new NakedDhcpv6Srv(0)) ); + + Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234)); + sol->setRemoteAddr(IOAddress("fe80::abcd")); + sol->addOption(generateIA(234, 1500, 3000)); + OptionPtr clientid = generateClientId(); + sol->addOption(clientid); + + // Pass it to the server and get an advertise + Pkt6Ptr reply = srv->processSolicit(sol); + + // check if we get response at all + checkResponse(reply, DHCPV6_ADVERTISE, 1234); - boost::shared_ptr ia = - boost::shared_ptr(new Option6IA(D6O_IA_NA, 234)); - ia->setT1(1501); - ia->setT2(2601); + // check that IA_NA was returned and that there's an address included + boost::shared_ptr addr = checkIA_NA(reply, 234, subnet_->getT1(), + subnet_->getT2()); + + // Check that the assigned address is indeed from the configured pool + checkIAAddr(addr, addr->getAddress(), subnet_->getPreferred(), subnet_->getValid()); + + // check DUIDs + checkServerId(reply, srv->getServerID()); + checkClientId(reply, clientid); + } + + // This test verifies that incoming SOLICIT can be handled properly, that an + // ADVERTISE is generated, that the response has an address and that address + // really belongs to the configured pool. + // + // This test sends a SOLICIT with IA_NA that contains a valid hint. + // + // constructed very simple SOLICIT message with: + // - client-id option (mandatory) + // - IA option (a request for address, with an address that belongs to the + // configured pool, i.e. is valid as hint) + // + // expected returned ADVERTISE message: + // - copy of client-id + // - server-id + // - IA that includes IAADDR + TEST_F(Dhcpv6SrvTest, SolicitHint) { + boost::scoped_ptr srv; + ASSERT_NO_THROW( srv.reset(new NakedDhcpv6Srv(0)) ); + + // Let's create a SOLICIT + Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234)); + sol->setRemoteAddr(IOAddress("fe80::abcd")); + boost::shared_ptr ia = generateIA(234, 1500, 3000); + + // with a valid hint + IOAddress hint("2001:db8:1:1::dead:beef"); + ASSERT_TRUE(subnet_->inPool(hint)); + OptionPtr hint_opt(new Option6IAAddr(D6O_IAADDR, hint, 300, 500)); + ia->addOption(hint_opt); sol->addOption(ia); + OptionPtr clientid = generateClientId(); + sol->addOption(clientid); + + // Pass it to the server and get an advertise + Pkt6Ptr reply = srv->processSolicit(sol); - // Let's not send address in solicit yet - // boost::shared_ptr addr(new Option6IAAddr(D6O_IAADDR, - // IOAddress("2001:db8:1234:ffff::ffff"), 5001, 7001)); - // ia->addOption(addr); - // sol->addOption(ia); + // check if we get response at all + checkResponse(reply, DHCPV6_ADVERTISE, 1234); + + OptionPtr tmp = reply->getOption(D6O_IA_NA); + ASSERT_TRUE(tmp); - // constructed very simple SOLICIT message with: - // - client-id option (mandatory) - // - IA option (a request for address, without any addresses) + // check that IA_NA was returned and that there's an address included + boost::shared_ptr addr = checkIA_NA(reply, 234, subnet_->getT1(), + subnet_->getT2()); - // expected returned ADVERTISE message: - // - copy of client-id - // - server-id - // - IA that includes IAADDR + // check that we've got the address we requested + checkIAAddr(addr, hint, subnet_->getPreferred(), subnet_->getValid()); - OptionPtr clientid = OptionPtr(new Option(Option::V6, D6O_CLIENTID, - clntDuid.begin(), - clntDuid.begin() + 16)); + // check DUIDs + checkServerId(reply, srv->getServerID()); + checkClientId(reply, clientid); + } + + // This test verifies that incoming SOLICIT can be handled properly, that an + // ADVERTISE is generated, that the response has an address and that address + // really belongs to the configured pool. + // + // This test sends a SOLICIT with IA_NA that contains an invalid hint. + // + // constructed very simple SOLICIT message with: + // - client-id option (mandatory) + // - IA option (a request for address, with an address that does not + // belong to the configured pool, i.e. is valid as hint) + // + // expected returned ADVERTISE message: + // - copy of client-id + // - server-id + // - IA that includes IAADDR + TEST_F(Dhcpv6SrvTest, SolicitInvalidHint) { + boost::scoped_ptr srv; + ASSERT_NO_THROW( srv.reset(new NakedDhcpv6Srv(0)) ); + + // Let's create a SOLICIT + Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234)); + sol->setRemoteAddr(IOAddress("fe80::abcd")); + boost::shared_ptr ia = generateIA(234, 1500, 3000); + IOAddress hint("2001:db8:1::cafe:babe"); + ASSERT_FALSE(subnet_->inPool(hint)); + OptionPtr hint_opt(new Option6IAAddr(D6O_IAADDR, hint, 300, 500)); + ia->addOption(hint_opt); + sol->addOption(ia); + OptionPtr clientid = generateClientId(); sol->addOption(clientid); - boost::shared_ptr reply = srv->processSolicit(sol); + // Pass it to the server and get an advertise + Pkt6Ptr reply = srv->processSolicit(sol); + + // check if we get response at all + checkResponse(reply, DHCPV6_ADVERTISE, 1234); + + // check that IA_NA was returned and that there's an address included + boost::shared_ptr addr = checkIA_NA(reply, 234, subnet_->getT1(), + subnet_->getT2()); + + // Check that the assigned address is indeed from the configured pool + checkIAAddr(addr, addr->getAddress(), subnet_->getPreferred(), subnet_->getValid()); + EXPECT_TRUE(subnet_->inPool(addr->getAddress())); + + // check DUIDs + checkServerId(reply, srv->getServerID()); + checkClientId(reply, clientid); + } + + // This test checks that the server is offering different addresses to different + // clients in ADVERTISEs. Please note that ADVERTISE is not a guarantee that such + // and address will be assigned. Had the pool was very small and contained only + // 2 addresses, the third client would get the same advertise as the first one + // and this is a correct behavior. It is REQUEST that will fail for the third + // client. ADVERTISE is basically saying "if you send me a request, you will + // probably get an address like this" (there are no guarantees). + TEST_F(Dhcpv6SrvTest, ManySolicits) { + boost::scoped_ptr srv; + ASSERT_NO_THROW( srv.reset(new NakedDhcpv6Srv(0)) ); + + Pkt6Ptr sol1 = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234)); + Pkt6Ptr sol2 = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 2345)); + Pkt6Ptr sol3 = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 3456)); + + sol1->setRemoteAddr(IOAddress("fe80::abcd")); + sol2->setRemoteAddr(IOAddress("fe80::1223")); + sol3->setRemoteAddr(IOAddress("fe80::3467")); + + sol1->addOption(generateIA(1, 1500, 3000)); + sol2->addOption(generateIA(2, 1500, 3000)); + sol3->addOption(generateIA(3, 1500, 3000)); + + // different client-id sizes + OptionPtr clientid1 = generateClientId(12); + OptionPtr clientid2 = generateClientId(14); + OptionPtr clientid3 = generateClientId(16); + + sol1->addOption(clientid1); + sol2->addOption(clientid2); + sol3->addOption(clientid3); + + // Pass it to the server and get an advertise + Pkt6Ptr reply1 = srv->processSolicit(sol1); + Pkt6Ptr reply2 = srv->processSolicit(sol2); + Pkt6Ptr reply3 = srv->processSolicit(sol3); // check if we get response at all - ASSERT_TRUE( reply != boost::shared_ptr() ); + checkResponse(reply1, DHCPV6_ADVERTISE, 1234); + checkResponse(reply2, DHCPV6_ADVERTISE, 2345); + checkResponse(reply3, DHCPV6_ADVERTISE, 3456); + + // check that IA_NA was returned and that there's an address included + boost::shared_ptr addr1 = checkIA_NA(reply1, 1, subnet_->getT1(), + subnet_->getT2()); + boost::shared_ptr addr2 = checkIA_NA(reply2, 2, subnet_->getT1(), + subnet_->getT2()); + boost::shared_ptr addr3 = checkIA_NA(reply3, 3, subnet_->getT1(), + subnet_->getT2()); + + // Check that the assigned address is indeed from the configured pool + checkIAAddr(addr1, addr1->getAddress(), subnet_->getPreferred(), subnet_->getValid()); + checkIAAddr(addr2, addr2->getAddress(), subnet_->getPreferred(), subnet_->getValid()); + checkIAAddr(addr3, addr3->getAddress(), subnet_->getPreferred(), subnet_->getValid()); + + // check DUIDs + checkServerId(reply1, srv->getServerID()); + checkServerId(reply2, srv->getServerID()); + checkServerId(reply3, srv->getServerID()); + checkClientId(reply1, clientid1); + checkClientId(reply2, clientid2); + checkClientId(reply3, clientid3); + + // Finally check that the addresses offered are different + EXPECT_NE(addr1->getAddress().toText(), addr2->getAddress().toText()); + EXPECT_NE(addr2->getAddress().toText(), addr3->getAddress().toText()); + EXPECT_NE(addr3->getAddress().toText(), addr1->getAddress().toText()); + cout << "Offered address to client1=" << addr1->getAddress().toText() << endl; + cout << "Offered address to client2=" << addr2->getAddress().toText() << endl; + cout << "Offered address to client3=" << addr3->getAddress().toText() << endl; + } + + + // This test verifies that incoming REQUEST can be handled properly, that a + // REPLY is generated, that the response has an address and that address + // really belongs to the configured pool. + // + // This test sends a REQUEST with IA_NA that contains a valid hint. + // + // constructed very simple REQUEST message with: + // - client-id option (mandatory) + // - IA option (a request for address, with an address that belongs to the + // configured pool, i.e. is valid as hint) + // + // expected returned REPLY message: + // - copy of client-id + // - server-id + // - IA that includes IAADDR + TEST_F(Dhcpv6SrvTest, RequestBasic) { + boost::scoped_ptr srv; + ASSERT_NO_THROW( srv.reset(new NakedDhcpv6Srv(0)) ); + + // Let's create a REQUEST + Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234)); + req->setRemoteAddr(IOAddress("fe80::abcd")); + boost::shared_ptr ia = generateIA(234, 1500, 3000); + + // with a valid hint + IOAddress hint("2001:db8:1:1::dead:beef"); + ASSERT_TRUE(subnet_->inPool(hint)); + OptionPtr hint_opt(new Option6IAAddr(D6O_IAADDR, hint, 300, 500)); + ia->addOption(hint_opt); + req->addOption(ia); + OptionPtr clientid = generateClientId(); + req->addOption(clientid); - EXPECT_EQ( DHCPV6_ADVERTISE, reply->getType() ); - EXPECT_EQ( 1234, reply->getTransid() ); + // Pass it to the server and hope for a REPLY + Pkt6Ptr reply = srv->processRequest(req); + + // check if we get response at all + checkResponse(reply, DHCPV6_REPLY, 1234); OptionPtr tmp = reply->getOption(D6O_IA_NA); - ASSERT_TRUE( tmp ); + ASSERT_TRUE(tmp); - Option6IA* reply_ia = dynamic_cast(tmp.get()); - EXPECT_EQ( 234, reply_ia->getIAID() ); + // check that IA_NA was returned and that there's an address included + boost::shared_ptr addr = checkIA_NA(reply, 234, subnet_->getT1(), + subnet_->getT2()); - // check that there's an address included - EXPECT_TRUE( reply_ia->getOption(D6O_IAADDR)); + // check that we've got the address we requested + checkIAAddr(addr, hint, subnet_->getPreferred(), subnet_->getValid()); - // check that server included our own client-id - tmp = reply->getOption(D6O_CLIENTID); - ASSERT_TRUE( tmp ); - EXPECT_EQ(clientid->getType(), tmp->getType() ); - ASSERT_EQ(clientid->len(), tmp->len() ); + // check DUIDs + checkServerId(reply, srv->getServerID()); + checkClientId(reply, clientid); - EXPECT_TRUE( clientid->getData() == tmp->getData() ); + // check that the lease is really in the database + Lease6Ptr l = checkLease(duid_, reply->getOption(D6O_IA_NA), addr); + EXPECT_TRUE(l); - LeaseMgr::instance().deleteLease6(addr->getAddress()); ++ LeaseMgrFactory::instance().deleteLease6(addr->getAddress()); + } - // check that server included its server-id - tmp = reply->getOption(D6O_SERVERID); - EXPECT_EQ(tmp->getType(), srv->getServerID()->getType() ); - ASSERT_EQ(tmp->len(), srv->getServerID()->len() ); + // This test checks that the server is offering different addresses to different + // clients in REQUEST. Please note that ADVERTISE is not a guarantee that such + // and address will be assigned. Had the pool was very small and contained only + // 2 addresses, the third client would get the same advertise as the first one + // and this is a correct behavior. It is REQUEST that will fail for the third + // client. ADVERTISE is basically saying "if you send me a request, you will + // probably get an address like this" (there are no guarantees). + TEST_F(Dhcpv6SrvTest, ManyRequests) { + boost::scoped_ptr srv; + ASSERT_NO_THROW( srv.reset(new NakedDhcpv6Srv(0)) ); - EXPECT_TRUE(tmp->getData() == srv->getServerID()->getData()); + Pkt6Ptr req1 = Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234)); + Pkt6Ptr req2 = Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 2345)); + Pkt6Ptr req3 = Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 3456)); - // more checks to be implemented + req1->setRemoteAddr(IOAddress("fe80::abcd")); + req2->setRemoteAddr(IOAddress("fe80::1223")); + req3->setRemoteAddr(IOAddress("fe80::3467")); + + req1->addOption(generateIA(1, 1500, 3000)); + req2->addOption(generateIA(2, 1500, 3000)); + req3->addOption(generateIA(3, 1500, 3000)); + + // different client-id sizes + OptionPtr clientid1 = generateClientId(12); + OptionPtr clientid2 = generateClientId(14); + OptionPtr clientid3 = generateClientId(16); + + req1->addOption(clientid1); + req2->addOption(clientid2); + req3->addOption(clientid3); + + // Pass it to the server and get an advertise + Pkt6Ptr reply1 = srv->processRequest(req1); + Pkt6Ptr reply2 = srv->processRequest(req2); + Pkt6Ptr reply3 = srv->processRequest(req3); + + // check if we get response at all + checkResponse(reply1, DHCPV6_REPLY, 1234); + checkResponse(reply2, DHCPV6_REPLY, 2345); + checkResponse(reply3, DHCPV6_REPLY, 3456); + + // check that IA_NA was returned and that there's an address included + boost::shared_ptr addr1 = checkIA_NA(reply1, 1, subnet_->getT1(), + subnet_->getT2()); + boost::shared_ptr addr2 = checkIA_NA(reply2, 2, subnet_->getT1(), + subnet_->getT2()); + boost::shared_ptr addr3 = checkIA_NA(reply3, 3, subnet_->getT1(), + subnet_->getT2()); + + // Check that the assigned address is indeed from the configured pool + checkIAAddr(addr1, addr1->getAddress(), subnet_->getPreferred(), subnet_->getValid()); + checkIAAddr(addr2, addr2->getAddress(), subnet_->getPreferred(), subnet_->getValid()); + checkIAAddr(addr3, addr3->getAddress(), subnet_->getPreferred(), subnet_->getValid()); + + // check DUIDs + checkServerId(reply1, srv->getServerID()); + checkServerId(reply2, srv->getServerID()); + checkServerId(reply3, srv->getServerID()); + checkClientId(reply1, clientid1); + checkClientId(reply2, clientid2); + checkClientId(reply3, clientid3); + + // Finally check that the addresses offered are different + EXPECT_NE(addr1->getAddress().toText(), addr2->getAddress().toText()); + EXPECT_NE(addr2->getAddress().toText(), addr3->getAddress().toText()); + EXPECT_NE(addr3->getAddress().toText(), addr1->getAddress().toText()); + cout << "Assigned address to client1=" << addr1->getAddress().toText() << endl; + cout << "Assigned address to client2=" << addr2->getAddress().toText() << endl; + cout << "Assigned address to client3=" << addr3->getAddress().toText() << endl; } + TEST_F(Dhcpv6SrvTest, serverReceivedPacketName) { // Check all possible packet types for (int itype = 0; itype < 256; ++itype) { diff --cc src/lib/dhcp/Makefile.am index 8cede9bba8,0d54a07acf..904bce30ba --- a/src/lib/dhcp/Makefile.am +++ b/src/lib/dhcp/Makefile.am @@@ -18,19 -15,24 +18,23 @@@ CLEANFILES = *.gcno *.gcd lib_LTLIBRARIES = libb10-dhcp++.la libb10-dhcpsrv.la libb10_dhcp___la_SOURCES = - libb10_dhcp___la_SOURCES += dhcp6.h dhcp4.h + libb10_dhcp___la_SOURCES += libdhcp++.cc libdhcp++.h libb10_dhcp___la_SOURCES += iface_mgr.cc iface_mgr.h -libb10_dhcp___la_SOURCES += iface_mgr_linux.cc libb10_dhcp___la_SOURCES += iface_mgr_bsd.cc +libb10_dhcp___la_SOURCES += iface_mgr_linux.cc libb10_dhcp___la_SOURCES += iface_mgr_sun.cc - libb10_dhcp___la_SOURCES += libdhcp++.cc libdhcp++.h - libb10_dhcp___la_SOURCES += option4_addrlst.cc option4_addrlst.h - libb10_dhcp___la_SOURCES += option6_addrlst.cc option6_addrlst.h - libb10_dhcp___la_SOURCES += option6_iaaddr.cc option6_iaaddr.h - libb10_dhcp___la_SOURCES += option6_ia.cc option6_ia.h libb10_dhcp___la_SOURCES += option.cc option.h - libb10_dhcp___la_SOURCES += pkt4.cc pkt4.h + libb10_dhcp___la_SOURCES += option_data_types.h + libb10_dhcp___la_SOURCES += option_definition.cc option_definition.h + libb10_dhcp___la_SOURCES += option6_ia.cc option6_ia.h + libb10_dhcp___la_SOURCES += option6_iaaddr.cc option6_iaaddr.h + libb10_dhcp___la_SOURCES += option6_addrlst.cc option6_addrlst.h + libb10_dhcp___la_SOURCES += option4_addrlst.cc option4_addrlst.h + libb10_dhcp___la_SOURCES += option6_int.h + libb10_dhcp___la_SOURCES += option6_int_array.h + libb10_dhcp___la_SOURCES += dhcp6.h dhcp4.h libb10_dhcp___la_SOURCES += pkt6.cc pkt6.h + libb10_dhcp___la_SOURCES += pkt4.cc pkt4.h -libb10_dhcp___la_SOURCES += duid.cc duid.h libb10_dhcp___la_CXXFLAGS = $(AM_CXXFLAGS) libb10_dhcp___la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES) @@@ -38,28 -40,23 +42,32 @@@ libb10_dhcp___la_LIBADD = $(top_build libb10_dhcp___la_LIBADD += $(top_builddir)/src/lib/util/libb10-util.la libb10_dhcp___la_LDFLAGS = -no-undefined -version-info 2:0:0 --libb10_dhcpsrv_la_SOURCES = cfgmgr.cc cfgmgr.h ++libb10_dhcpsrv_la_SOURCES = +libb10_dhcpsrv_la_SOURCES += addr_utilities.cc addr_utilities.h ++libb10_dhcpsrv_la_SOURCES += alloc_engine.cc alloc_engine.h ++libb10_dhcpsrv_la_SOURCES += cfgmgr.cc cfgmgr.h +libb10_dhcpsrv_la_SOURCES += duid.cc duid.h +libb10_dhcpsrv_la_SOURCES += lease_mgr.cc lease_mgr.h +libb10_dhcpsrv_la_SOURCES += lease_mgr_factory.cc lease_mgr_factory.h ++libb10_dhcpsrv_la_SOURCES += memfile_lease_mgr.cc memfile_lease_mgr.h +if HAVE_MYSQL +libb10_dhcpsrv_la_SOURCES += mysql_lease_mgr.cc mysql_lease_mgr.h +endif libb10_dhcpsrv_la_SOURCES += pool.cc pool.h libb10_dhcpsrv_la_SOURCES += subnet.cc subnet.h libb10_dhcpsrv_la_SOURCES += triplet.h -libb10_dhcpsrv_la_SOURCES += lease_mgr.cc lease_mgr.h -libb10_dhcpsrv_la_SOURCES += memfile_lease_mgr.cc memfile_lease_mgr.h -libb10_dhcpsrv_la_SOURCES += addr_utilities.cc addr_utilities.h -libb10_dhcpsrv_la_SOURCES += alloc_engine.cc alloc_engine.h libb10_dhcpsrv_la_CXXFLAGS = $(AM_CXXFLAGS) libb10_dhcpsrv_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES) - libb10_dhcpsrv_la_LIBADD = $(top_builddir)/src/lib/asiolink/libb10-asiolink.la + libb10_dhcpsrv_la_LIBADD = $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la + libb10_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la libb10_dhcpsrv_la_LIBADD += $(top_builddir)/src/lib/util/libb10-util.la libb10_dhcpsrv_la_LDFLAGS = -no-undefined -version-info 2:0:0 +if HAVE_MYSQL +libb10_dhcpsrv_la_LDFLAGS += $(MYSQL_LIBS) +endif -EXTRA_DIST = README +EXTRA_DIST = README database_backends.dox if USE_CLANGPP # Disable unused parameter warning caused by some of the diff --cc src/lib/dhcp/addr_utilities.h index 15532d0543,15532d0543..a1d856c632 --- a/src/lib/dhcp/addr_utilities.h +++ b/src/lib/dhcp/addr_utilities.h @@@ -12,6 -12,6 +12,9 @@@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. ++#ifndef ADDR_UTILITIES_H ++#define ADDR_UTILITIES_H ++ #include namespace isc { @@@ -51,3 -51,3 +54,5 @@@ isc::asiolink::IOAddress lastAddrInPref }; }; ++ ++#endif // ADDR_UTILITIES_H diff --cc src/lib/dhcp/alloc_engine.cc index 0000000000,650fd5f98f..53ef7f3036 mode 000000,100644..100644 --- a/src/lib/dhcp/alloc_engine.cc +++ b/src/lib/dhcp/alloc_engine.cc @@@ -1,0 -1,275 +1,276 @@@ + // 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 + + using namespace isc::asiolink; + + namespace isc { + namespace dhcp { + + AllocEngine::IterativeAllocator::IterativeAllocator() + :Allocator() { + } + + isc::asiolink::IOAddress + AllocEngine::IterativeAllocator::increaseAddress(const isc::asiolink::IOAddress& addr) { + uint8_t packed[V6ADDRESS_LEN]; + int len; + + // First we copy the whole address as 16 bytes. + if (addr.getFamily()==AF_INET) { + // IPv4 + std::memcpy(packed, addr.getAddress().to_v4().to_bytes().data(), 4); + len = 4; + } else { + // IPv6 + std::memcpy(packed, addr.getAddress().to_v6().to_bytes().data(), 16); + len = 16; + } + + for (int i = len - 1; i >= 0; --i) { + ++packed[i]; + if (packed[i] != 0) { + break; + } + } + + return (IOAddress::from_bytes(addr.getFamily(), packed)); + } + + + isc::asiolink::IOAddress + AllocEngine::IterativeAllocator::pickAddress(const Subnet6Ptr& subnet, + const DuidPtr&, + const IOAddress&) { + + // Let's get the last allocated address. It is usually set correctly, + // but there are times when it won't be (like after removing a pool or + // perhaps restaring the server). + IOAddress last = subnet->getLastAllocated(); + + const Pool6Collection& pools = subnet->getPools(); + + if (pools.size() == 0) { + isc_throw(AllocFailed, "No pools defined in selected subnet"); + } + + // first we need to find a pool the last address belongs to. + Pool6Collection::const_iterator it; + for (it = pools.begin(); it != pools.end(); ++it) { + if ((*it)->inRange(last)) { + break; + } + } + + // last one was bogus for one of several reasons: + // - we just booted up and that's the first address we're allocating + // - a subnet was removed or other reconfiguration just completed + // - perhaps allocation algorithm was changed + if (it == pools.end()) { + // ok to access first element directly. We checked that pools is non-empty + IOAddress next = pools[0]->getFirstAddress(); + subnet->setLastAllocated(next); + return (next); + } + + // Ok, we have a pool that the last address belonged to, let's use it. + + IOAddress next = increaseAddress(last); // basically addr++ + if ((*it)->inRange(next)) { + // the next one is in the pool as well, so we haven't hit pool boundary yet + subnet->setLastAllocated(next); + return (next); + } + + // We hit pool boundary, let's try to jump to the next pool and try again + ++it; + if (it == pools.end()) { + // Really out of luck today. That was the last pool. Let's rewind + // to the beginning. + next = pools[0]->getFirstAddress(); + subnet->setLastAllocated(next); + return (next); + } + + // there is a next pool, let's try first adddress from it + next = (*it)->getFirstAddress(); + subnet->setLastAllocated(next); + return (next); + } + + AllocEngine::HashedAllocator::HashedAllocator() + :Allocator() { + isc_throw(NotImplemented, "Hashed allocator is not implemented"); + } + + + isc::asiolink::IOAddress + AllocEngine::HashedAllocator::pickAddress(const Subnet6Ptr&, + const DuidPtr&, + const IOAddress&) { + isc_throw(NotImplemented, "Hashed allocator is not implemented"); + } + + AllocEngine::RandomAllocator::RandomAllocator() + :Allocator() { + isc_throw(NotImplemented, "Random allocator is not implemented"); + } + + + isc::asiolink::IOAddress + AllocEngine::RandomAllocator::pickAddress(const Subnet6Ptr&, + const DuidPtr&, + const IOAddress&) { + isc_throw(NotImplemented, "Random allocator is not implemented"); + } + + + AllocEngine::AllocEngine(AllocType engine_type, unsigned int attempts) + :attempts_(attempts) { + switch (engine_type) { + case ALLOC_ITERATIVE: + allocator_ = boost::shared_ptr(new IterativeAllocator()); + break; + case ALLOC_HASHED: + allocator_ = boost::shared_ptr(new HashedAllocator()); + break; + case ALLOC_RANDOM: + allocator_ = boost::shared_ptr(new RandomAllocator()); + break; + + default: + isc_throw(BadValue, "Invalid/unsupported allocation algorithm"); + } + } + + Lease6Ptr + AllocEngine::allocateAddress6(const Subnet6Ptr& subnet, + const DuidPtr& duid, + uint32_t iaid, + const IOAddress& hint, + bool fake_allocation /* = false */ ) { + + // That check is not necessary. We create allocator in AllocEngine + // constructor + if (!allocator_) { + isc_throw(InvalidOperation, "No allocator selected"); + } + + // check if there's existing lease for that subnet/duid/iaid combination. - Lease6Ptr existing = LeaseMgr::instance().getLease6(*duid, iaid, subnet->getID()); ++ Lease6Ptr existing = LeaseMgrFactory::instance().getLease6(*duid, iaid, subnet->getID()); + if (existing) { + // we have a lease already. This is a returning client, probably after + // his reboot. + return (existing); + } + + // check if the hint is in pool and is available + if (subnet->inPool(hint)) { - existing = LeaseMgr::instance().getLease6(hint); ++ existing = LeaseMgrFactory::instance().getLease6(hint); + if (!existing) { + /// @todo: check if the hint is reserved once we have host support + /// implemented + + // the hint is valid and not currently used, let's create a lease for it + Lease6Ptr lease = createLease(subnet, duid, iaid, hint, fake_allocation); + + // It can happen that the lease allocation failed (we could have lost + // the race condition. That means that the hint is lo longer usable and + // we need to continue the regular allocation path. + if (lease) { + return (lease); + } + } + } + + unsigned int i = attempts_; + do { + IOAddress candidate = allocator_->pickAddress(subnet, duid, hint); + + /// @todo: check if the address is reserved once we have host support + /// implemented + - Lease6Ptr existing = LeaseMgr::instance().getLease6(candidate); ++ Lease6Ptr existing = LeaseMgrFactory::instance().getLease6(candidate); + // there's no existing lease for selected candidate, so it is + // free. Let's allocate it. + if (!existing) { + Lease6Ptr lease = createLease(subnet, duid, iaid, candidate, + fake_allocation); + if (lease) { + return (lease); + } + + // Although the address was free just microseconds ago, it may have + // been taken just now. If the lease insertion fails, we continue + // allocation attempts. + } + + // continue trying allocation until we run out of attempts + // (or attempts are set to 0, which means infinite) + --i; + } while ( i || !attempts_); + + isc_throw(AllocFailed, "Failed to allocate address after " << attempts_ + << " tries"); + } + + Lease6Ptr AllocEngine::createLease(const Subnet6Ptr& subnet, + const DuidPtr& duid, + uint32_t iaid, + const IOAddress& addr, + bool fake_allocation /*= false */ ) { + + Lease6Ptr lease(new Lease6(Lease6::LEASE_IA_NA, addr, duid, iaid, + subnet->getPreferred(), subnet->getValid(), + subnet->getT1(), subnet->getT2(), subnet->getID())); + + if (!fake_allocation) { + // That is a real (REQUEST) allocation - bool status = LeaseMgr::instance().addLease(lease); ++ bool status = LeaseMgrFactory::instance().addLease(lease); + + if (status) { + + return (lease); + } else { + // One of many failures with LeaseMgr (e.g. lost connection to the + // database, database failed etc.). One notable case for that + // is that we are working in multi-process mode and we lost a race + // (some other process got that address first) + return (Lease6Ptr()); + } + } else { + // That is only fake (SOLICIT without rapid-commit) allocation + + // It is for advertise only. We should not insert the lease into LeaseMgr, + // but rather check that we could have inserted it. - Lease6Ptr existing = LeaseMgr::instance().getLease6(addr); ++ Lease6Ptr existing = LeaseMgrFactory::instance().getLease6(addr); + if (!existing) { + return (lease); + } else { + return (Lease6Ptr()); + } + } + } + + AllocEngine::~AllocEngine() { + // no need to delete allocator. smart_ptr will do the trick for us + } + + }; // end of isc::dhcp namespace + }; // end of isc namespace diff --cc src/lib/dhcp/cfgmgr.h index 2911d059de,2911d059de..ea8125fa06 --- a/src/lib/dhcp/cfgmgr.h +++ b/src/lib/dhcp/cfgmgr.h @@@ -162,4 -162,4 +162,4 @@@ protected } // namespace isc::dhcp } // namespace isc --#endif ++#endif // CFGMGR_H diff --cc src/lib/dhcp/dhcp6.h index 15c306da66,22e7c8d0e0..116019e3fe --- a/src/lib/dhcp/dhcp6.h +++ b/src/lib/dhcp/dhcp6.h @@@ -190,4 -191,4 +191,4 @@@ extern const int dhcpv6_type_name_max #define IRT_DEFAULT 86400 #define IRT_MINIMUM 600 --#endif ++#endif /* DHCP6_H */ diff --cc src/lib/dhcp/iface_mgr.h index a7b9a789ff,a7b9a789ff..1b15595a82 --- a/src/lib/dhcp/iface_mgr.h +++ b/src/lib/dhcp/iface_mgr.h @@@ -681,4 -681,4 +681,4 @@@ private }; // namespace isc::dhcp }; // namespace isc --#endif ++#endif // IFACE_MGR_H diff --cc src/lib/dhcp/lease_mgr.cc index 809c528ef3,59582fb115..8fed9169a5 --- a/src/lib/dhcp/lease_mgr.cc +++ b/src/lib/dhcp/lease_mgr.cc @@@ -12,67 -12,96 +12,94 @@@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. -#include "lease_mgr.h" +#include + #include ++ + #include + #include -#include ++ ++#include + #include ++#include + #include -#include -#include + #include -#include -#include ++#include ++ + #include using namespace std; using namespace isc::dhcp; - LeaseMgr::LeaseMgr(const LeaseMgr::ParameterMap& parameters) - : parameters_(parameters) { - } -LeaseMgr* LeaseMgr::instance_ = NULL; - + Lease6::Lease6(LeaseType type, const isc::asiolink::IOAddress& addr, DuidPtr duid, + uint32_t iaid, uint32_t preferred, uint32_t valid, uint32_t t1, + uint32_t t2, SubnetID subnet_id, uint8_t prefixlen) + :type_(type), addr_(addr), prefixlen_(prefixlen), iaid_(iaid), duid_(duid), + preferred_lft_(preferred), valid_lft_(valid), t1_(t1), t2_(t2), + subnet_id_(subnet_id), fixed_(false), fqdn_fwd_(false), + fqdn_rev_(false) { + if (!duid) { + isc_throw(InvalidOperation, "DUID must be specified for a lease"); + } - LeaseMgr::~LeaseMgr() { + cltt_ = time(NULL); } -LeaseMgr& LeaseMgr::instance() { - if (!instance_) { - isc_throw(InvalidOperation, "LeaseManager not instantiated yet"); +std::string LeaseMgr::getParameter(const std::string& name) const { + ParameterMap::const_iterator param = parameters_.find(name); + if (param == parameters_.end()) { + isc_throw(BadValue, "Parameter not found"); } - return (*instance_); + return (param->second); } -void LeaseMgr::destroy_instance() { - if (!instance_) { - isc_throw(InvalidOperation, "LeaseManager not instantiated yet"); +std::string +Lease6::toText() { + ostringstream stream; + + stream << "Type: " << static_cast(type_) << " ("; + switch (type_) { + case Lease6::LEASE_IA_NA: + stream << "IA_NA)\n"; + break; + case Lease6::LEASE_IA_TA: + stream << "IA_TA)\n"; + break; + case Lease6::LEASE_IA_PD: + stream << "IA_PD)\n"; + break; + default: + stream << "unknown)\n"; } - delete instance_; - instance_ = NULL; + stream << "Address: " << addr_.toText() << "\n" + << "Prefix length: " << static_cast(prefixlen_) << "\n" + << "IAID: " << iaid_ << "\n" + << "Pref life: " << preferred_lft_ << "\n" + << "Valid life: " << valid_lft_ << "\n" + << "Cltt: " << cltt_ << "\n" + << "Subnet ID: " << subnet_id_ << "\n"; + + return (stream.str()); } -LeaseMgr::LeaseMgr(const std::string& dbconfig) { - if (instance_) { - isc_throw(InvalidOperation, "LeaseManager already instantiated"); - } - - // remember the pointer to the singleton instance - instance_ = this; - - if (dbconfig.length() == 0) { - return; - } - - vector tokens; - - // we need to pass a string to is_any_of, not just char *. Otherwise there - // are cryptic warnings on Debian6 running g++ 4.4 in /usr/include/c++/4.4 - // /bits/stl_algo.h:2178 "array subscript is above array bounds" - boost::split(tokens, dbconfig, boost::is_any_of( string("\t ") )); - BOOST_FOREACH(std::string token, tokens) { - size_t pos = token.find("="); - if (pos != string::npos) { - string name = token.substr(0, pos); - string value = token.substr(pos + 1); - parameters_.insert(pair(name, value)); - } else { - isc_throw(InvalidParameter, "Cannot parse " << token - << ", expected format is name=value"); - } - - } +bool +Lease6::operator==(const Lease6& other) const { + return ( + type_ == other.type_ && + addr_ == other.addr_ && + prefixlen_ == other.prefixlen_ && + iaid_ == other.iaid_ && + *duid_ == *other.duid_ && + preferred_lft_ == other.preferred_lft_ && + valid_lft_ == other.valid_lft_ && + cltt_ == other.cltt_ && + subnet_id_ == other.subnet_id_ + ); } + -std::string LeaseMgr::getParameter(const std::string& name) const { - std::map::const_iterator param - = parameters_.find(name); - if (param == parameters_.end()) { - isc_throw(BadValue, "Parameter not found"); - } - return (param->second); ++LeaseMgr::LeaseMgr(const LeaseMgr::ParameterMap& parameters) ++ : parameters_(parameters) { + } + + LeaseMgr::~LeaseMgr() { - instance_ = NULL; + } diff --cc src/lib/dhcp/lease_mgr.h index 4b7c60753e,e859deaf64..05eca4dd05 --- a/src/lib/dhcp/lease_mgr.h +++ b/src/lib/dhcp/lease_mgr.h @@@ -15,18 -15,17 +15,21 @@@ #ifndef LEASE_MGR_H #define LEASE_MGR_H -#include -#include -#include -#include + #include -#include -#include -#include + #include ++#include + #include ++#include ++ ++#include ++#include ++ +#include +#include +#include +#include +#include - #include - #include - #include - #include - #include - /// @file dhcp/lease_mgr.h /// @brief An abstract API for lease database /// @@@ -63,38 -60,6 +66,34 @@@ namespace isc { namespace dhcp { +/// @brief Exception thrown if name of database is not specified +class NoDatabaseName : public Exception { +public: + NoDatabaseName(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) {} +}; + +/// @brief Exception thrown on failure to open database +class DbOpenError : public Exception { +public: + DbOpenError(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) {} +}; + +/// @brief Exception thrown on failure to execute a database function +class DbOperationError : public Exception { +public: + DbOperationError(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) {} +}; + +/// @brief Attempt to update lease that was not there +class NoSuchLease : public Exception { +public: + NoSuchLease(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) {} +}; + - /// @brief specifies unique subnet identifier - /// @todo: Move this to subnet.h once ticket #2237 is merged - typedef uint32_t SubnetID; - /// @brief Structure that holds a lease for IPv4 address /// /// For performance reasons it is a simple structure, not a class. If we chose @@@ -325,26 -276,31 +330,25 @@@ typedef std::vector Lease6Co /// be used directly, but rather specialized derived class should be used /// instead. /// -/// This class is a meta-singleton. At any given time, there is only one -/// instance of any classes derived from that class. That is achieved with -/// defining only a single protected constructor, so every derived class has -/// to use it. Furthermore, this sole constructor registers the first instance -/// (and throws InvalidOperation if there is an attempt to create a second one). -class LeaseMgr : public boost::noncopyable { +/// As all methods are virtual, this class throws no exceptions. However, +/// methods in concrete implementations of this class may throw exceptions: +/// see the documentation of those classes for details. +class LeaseMgr { public: - /// Client Hardware address typedef std::vector HWAddr; - /// @brief returns a single instance of LeaseMgr - /// - /// LeaseMgr is a singleton and this method is the only way of - /// accessing it. LeaseMgr must be created first. See - /// isc::dhcp::LeaseMgrFactory class (work of ticket #2342. - /// Otherwise instance() will throw InvalidOperation exception. - /// @throw InvalidOperation if LeaseMgr not instantiated - static LeaseMgr& instance(); + /// Database configuration parameter map + typedef std::map ParameterMap; - /// @brief The sole lease manager constructor - /// @brief destroys the only instance of LeaseMgr ++ /// @brief Constructor /// - /// This method is used mostly in tests, where LeaseMgr is destroyed - /// at the end of each test, just to be created at the beginning of - /// the next one. - static void destroy_instance(); + /// @param parameters A data structure relating keywords and values + /// concerned with the database. + LeaseMgr(const ParameterMap& parameters); + - /// @brief Destructor (closes file) - virtual ~LeaseMgr(); ++ /// @brief Destructor ++ ~LeaseMgr(); /// @brief Adds an IPv4 lease. /// @@@ -524,28 -473,32 +528,28 @@@ /// B>=A and B=C (it is ok to have newer backend, as it should be backward /// compatible) /// Also if B>C, some database upgrade procedure may be triggered - virtual std::string getVersion() const = 0; - - /// @todo: Add host management here - /// As host reservation is outside of scope for 2012, support for hosts - /// is currently postponed. + virtual std::pair getVersion() const = 0; -protected: - /// @brief The sole lease manager constructor + /// @brief Commit Transactions /// - /// dbconfig is a generic way of passing parameters. Parameters are passed - /// in the "name=value" format, separated by spaces. Values may be enclosed - /// in double quotes, if needed. This ctor guarantees that there will be - /// only one instance of any derived classes. If there is a second instance - /// being created with the first one still around, it will throw - /// InvalidOperation. + /// Commits all pending database operations. On databases that don't + /// support transactions, this is a no-op. + virtual void commit() = 0; + + /// @brief Rollback Transactions /// - /// @param dbconfig database configuration - /// @throw InvalidOperation when trying to create second LeaseMgr - LeaseMgr(const std::string& dbconfig); + /// Rolls back all pending database operations. On databases that don't + /// support transactions, this is a no-op. + virtual void rollback() = 0; - /// @brief Destructor - virtual ~LeaseMgr(); + /// @todo: Add host management here + /// As host reservation is outside of scope for 2012, support for hosts + /// is currently postponed. - protected: /// @brief returns value of the parameter std::string getParameter(const std::string& name) const; ++private: /// @brief list of parameters passed in dbconfig /// /// That will be mostly used for storing database name, username, diff --cc src/lib/dhcp/lease_mgr_factory.cc index adc8ce0a36,0000000000..08a04e4978 mode 100644,000000..100644 --- a/src/lib/dhcp/lease_mgr_factory.cc +++ b/src/lib/dhcp/lease_mgr_factory.cc @@@ -1,114 -1,0 +1,119 @@@ +// 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 "config.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + ++#include +#ifdef HAVE_MYSQL +#include +#endif + +using namespace std; + +namespace isc { +namespace dhcp { + +boost::scoped_ptr& +LeaseMgrFactory::getLeaseMgrPtr() { + static boost::scoped_ptr leaseMgrPtr; + return (leaseMgrPtr); +} + +LeaseMgr::ParameterMap +LeaseMgrFactory::parse(const std::string& dbconfig) { + LeaseMgr::ParameterMap mapped_tokens; + + if (! dbconfig.empty()) { + vector tokens; + + // We need to pass a string to is_any_of, not just char*. Otherwise + // there are cryptic warnings on Debian6 running g++ 4.4 in + // /usr/include/c++/4.4/bits/stl_algo.h:2178 "array subscript is above + // array bounds" + boost::split(tokens, dbconfig, boost::is_any_of( string("\t ") )); + BOOST_FOREACH(std::string token, tokens) { + size_t pos = token.find("="); + if (pos != string::npos) { + string name = token.substr(0, pos); + string value = token.substr(pos + 1); + mapped_tokens.insert(make_pair(name, value)); + } else { + isc_throw(InvalidParameter, "Cannot parse " << token + << ", expected format is name=value"); + } + } + } + + return (mapped_tokens); +} + +void +LeaseMgrFactory::create(const std::string& dbconfig) { + const std::string type = "type"; + + // Is "type" present? + LeaseMgr::ParameterMap parameters = parse(dbconfig); + if (parameters.find(type) == parameters.end()) { + isc_throw(InvalidParameter, "Database configuration parameters do not " + "contain the 'type' keyword"); + } + + // Yes, check what it is. +#ifdef HAVE_MYSQL + if (parameters[type] == string("mysql")) { + getLeaseMgrPtr().reset(new MySqlLeaseMgr(parameters)); + return; + } +#endif ++ if (parameters[type] == string("memfile")) { ++ getLeaseMgrPtr().reset(new Memfile_LeaseMgr(parameters)); ++ return; ++ } + + // Get here on no match + isc_throw(InvalidType, "Database configuration parameter 'type' does " + "not specify a supported database backend"); +} + +void +LeaseMgrFactory::destroy() { + getLeaseMgrPtr().reset(); +} + +LeaseMgr& +LeaseMgrFactory::instance() { + LeaseMgr* lmptr = getLeaseMgrPtr().get(); + if (lmptr == NULL) { + isc_throw(NoLeaseManager, "no current lease manager is available"); + } + return (*lmptr); +} + + +}; // namespace dhcp +}; // namespace isc diff --cc src/lib/dhcp/memfile_lease_mgr.cc index 0000000000,63023c402a..1424b39778 mode 000000,100644..100644 --- a/src/lib/dhcp/memfile_lease_mgr.cc +++ b/src/lib/dhcp/memfile_lease_mgr.cc @@@ -1,0 -1,124 +1,132 @@@ + // 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 "memfile_lease_mgr.h" ++ ++#include + + using namespace isc::dhcp; -using namespace isc::dhcp::test; + -Memfile_LeaseMgr::Memfile_LeaseMgr(const std::string& dbconfig) - : LeaseMgr(dbconfig) { ++Memfile_LeaseMgr::Memfile_LeaseMgr(const ParameterMap& parameters) ++ : LeaseMgr(parameters) { + std::cout << "Warning: Using memfile database backend. It is usable for" << std::endl; + std::cout << "Warning: limited testing only. File support not implemented yet." << std::endl; + std::cout << "Warning: Leases will be lost after restart." << std::endl; + } + + Memfile_LeaseMgr::~Memfile_LeaseMgr() { + } + + bool Memfile_LeaseMgr::addLease(const Lease4Ptr&) { + return (false); + } + + bool Memfile_LeaseMgr::addLease(const Lease6Ptr& lease) { + if (getLease6(lease->addr_)) { + // there is a lease with specified address already + return (false); + } + storage6_.insert(lease); + return (true); + } + -Lease4Ptr Memfile_LeaseMgr::getLease4(isc::asiolink::IOAddress) const { ++Lease4Ptr Memfile_LeaseMgr::getLease4(const isc::asiolink::IOAddress&) const { + return (Lease4Ptr()); + } + + Lease4Collection Memfile_LeaseMgr::getLease4(const HWAddr& ) const { + return (Lease4Collection()); + } + -Lease4Ptr Memfile_LeaseMgr::getLease4(isc::asiolink::IOAddress , ++Lease4Ptr Memfile_LeaseMgr::getLease4(const isc::asiolink::IOAddress&, + SubnetID) const { + return (Lease4Ptr()); + } + + Lease4Ptr Memfile_LeaseMgr::getLease4(const HWAddr&, + SubnetID) const { + return (Lease4Ptr()); + } + + + Lease4Ptr Memfile_LeaseMgr::getLease4(const ClientId&, + SubnetID) const { + return (Lease4Ptr()); + } + + Lease4Collection Memfile_LeaseMgr::getLease4(const ClientId& ) const { + return (Lease4Collection()); + } + + Lease6Ptr Memfile_LeaseMgr::getLease6(const isc::asiolink::IOAddress& addr) const { + Lease6Storage::iterator l = storage6_.find(addr); + if (l == storage6_.end()) { + return (Lease6Ptr()); + } else { + return (*l); + } + } + + Lease6Collection Memfile_LeaseMgr::getLease6(const DUID& , uint32_t ) const { + return (Lease6Collection()); + } + + Lease6Ptr Memfile_LeaseMgr::getLease6(const DUID& duid, uint32_t iaid, + SubnetID subnet_id) const { + /// @todo: Slow, naive implementation. Write it using additional indexes + for (Lease6Storage::iterator l = storage6_.begin(); l != storage6_.end(); ++l) { + if ( (*((*l)->duid_) == duid) && + ( (*l)->iaid_ == iaid) && + ( (*l)->subnet_id_ == subnet_id)) { + return (*l); + } + } + return (Lease6Ptr()); + } + + void Memfile_LeaseMgr::updateLease4(const Lease4Ptr& ) { + } + + void Memfile_LeaseMgr::updateLease6(const Lease6Ptr& ) { + + } + -bool Memfile_LeaseMgr::deleteLease4(uint32_t ) { ++bool Memfile_LeaseMgr::deleteLease4(const isc::asiolink::IOAddress&) { + return (false); + } + + bool Memfile_LeaseMgr::deleteLease6(const isc::asiolink::IOAddress& addr) { + Lease6Storage::iterator l = storage6_.find(addr); + if (l == storage6_.end()) { + // no such lease + return (false); + } else { + storage6_.erase(l); + return (true); + } + } + + std::string Memfile_LeaseMgr::getDescription() const { + return (std::string("This is a dummy memfile backend implementation.\n" + "It does not offer any useful lease management and its only\n" + "purpose is to test abstract lease manager API.")); + } ++ ++void ++Memfile_LeaseMgr::commit() { ++} ++ ++void ++Memfile_LeaseMgr::rollback() { ++} diff --cc src/lib/dhcp/memfile_lease_mgr.h index 0000000000,c5a41e6c7f..e3f415d36d mode 000000,100644..100644 --- a/src/lib/dhcp/memfile_lease_mgr.h +++ b/src/lib/dhcp/memfile_lease_mgr.h @@@ -1,0 -1,228 +1,241 @@@ + // 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. + + #ifndef MEMFILE_LEASE_MGR_H + #define MEMFILE_LEASE_MGR_H + -#include + #include + #include + #include + #include + ++#include ++ + namespace isc { + namespace dhcp { -namespace test { + + // This is a concrete implementation of a Lease database. + // + // It is for testing purposes only. It is NOT a production code. + // + // It does not do anything useful now, and is used for abstract LeaseMgr + // class testing. It may later evolve into more useful backend if the + // need arises. We can reuse code from memfile benchmark. See code in + // tests/tools/dhcp-ubench/memfile_bench.{cc|h} + class Memfile_LeaseMgr : public LeaseMgr { + public: + + /// @brief The sole lease manager constructor + /// + /// dbconfig is a generic way of passing parameters. Parameters + /// are passed in the "name=value" format, separated by spaces. + /// Values may be enclosed in double quotes, if needed. + /// - /// @param dbconfig database configuration - Memfile_LeaseMgr(const std::string& dbconfig); ++ /// @param parameters A data structure relating keywords and values ++ /// concerned with the database. ++ Memfile_LeaseMgr(const ParameterMap& parameters); + + /// @brief Destructor (closes file) + virtual ~Memfile_LeaseMgr(); + + /// @brief Adds an IPv4 lease. + /// + /// @todo Not implemented yet + /// @param lease lease to be added + virtual bool addLease(const Lease4Ptr& lease); + + /// @brief Adds an IPv6 lease. + /// + /// @param lease lease to be added + virtual bool addLease(const Lease6Ptr& lease); + + /// @brief Returns existing IPv4 lease for specified IPv4 address. + /// + /// @todo Not implemented yet + /// @param addr address of the searched lease + /// + /// @return a collection of leases - virtual Lease4Ptr getLease4(isc::asiolink::IOAddress addr) const; ++ virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress& addr) const; + + /// @brief Returns existing IPv4 lease for specific address and subnet + /// + /// @todo Not implemented yet + /// @param addr address of the searched lease + /// @param subnet_id ID of the subnet the lease must belong to + /// + /// @return smart pointer to the lease (or NULL if a lease is not found) - virtual Lease4Ptr getLease4(isc::asiolink::IOAddress addr, ++ virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress& addr, + SubnetID subnet_id) const; + + /// @brief Returns existing IPv4 leases for specified hardware address. + /// + /// @todo Not implemented yet + /// + /// Although in the usual case there will be only one lease, for mobile + /// clients or clients with multiple static/fixed/reserved leases there + /// can be more than one. Thus return type is a container, not a single + /// pointer. + /// + /// @param hwaddr hardware address of the client + /// + /// @return lease collection + virtual Lease4Collection getLease4(const HWAddr& hwaddr) const; + + /// @brief Returns existing IPv4 leases for specified hardware address + /// and a subnet + /// + /// @todo Not implemented yet + /// + /// There can be at most one lease for a given HW address in a single + /// pool, so this method with either return a single lease or NULL. + /// + /// @param hwaddr hardware address of the client + /// @param subnet_id identifier of the subnet that lease must belong to + /// + /// @return a pointer to the lease (or NULL if a lease is not found) + virtual Lease4Ptr getLease4(const HWAddr& hwaddr, + SubnetID subnet_id) const; + + /// @brief Returns existing IPv4 lease for specified client-id + /// + /// @todo Not implemented yet + /// + /// @param clientid client identifier + virtual Lease4Collection getLease4(const ClientId& clientid) const; + + /// @brief Returns existing IPv4 lease for specified client-id + /// + /// There can be at most one lease for a given HW address in a single + /// pool, so this method with either return a single lease or NULL. + /// + /// @todo Not implemented yet + /// + /// @param clientid client identifier + /// @param subnet_id identifier of the subnet that lease must belong to + /// + /// @return a pointer to the lease (or NULL if a lease is not found) + virtual Lease4Ptr getLease4(const ClientId& clientid, + SubnetID subnet_id) const; + + /// @brief Returns existing IPv6 lease for a given IPv6 address. + /// + /// @param addr address of the searched lease + /// + /// @return smart pointer to the lease (or NULL if a lease is not found) + Lease6Ptr getLease6(const isc::asiolink::IOAddress& addr) const; + + /// @brief Returns existing IPv6 lease for a given DUID+IA combination + /// + /// @todo Not implemented yet + /// + /// @param duid client DUID + /// @param iaid IA identifier + /// + /// @return collection of IPv6 leases + Lease6Collection getLease6(const DUID& duid, uint32_t iaid) const; + + /// @brief Returns existing IPv6 lease for a given DUID+IA combination + /// + /// @todo Not implemented yet + /// + /// @param duid client DUID + /// @param iaid IA identifier + /// @param subnet_id identifier of the subnet the lease must belong to + /// + /// @return smart pointer to the lease (or NULL if a lease is not found) + Lease6Ptr getLease6(const DUID& duid, uint32_t iaid, SubnetID subnet_id) const; + + /// @brief Updates IPv4 lease. + /// + /// @todo Not implemented yet + /// + /// @param lease4 The lease to be updated. + /// + /// If no such lease is present, an exception will be thrown. + void updateLease4(const Lease4Ptr& lease4); + + /// @brief Updates IPv4 lease. + /// + /// @todo Not implemented yet + /// + /// @param lease4 The lease to be updated. + /// + /// If no such lease is present, an exception will be thrown. + void updateLease6(const Lease6Ptr& lease6); + + /// @brief Deletes a lease. + /// - /// @todo Not implemented yet - /// + /// @param addr IPv4 address of the lease to be deleted. + /// + /// @return true if deletion was successful, false if no such lease exists - bool deleteLease4(uint32_t addr); ++ virtual bool deleteLease4(const isc::asiolink::IOAddress& addr); + + /// @brief Deletes a lease. + /// + /// @param addr IPv4 address of the lease to be deleted. + /// + /// @return true if deletion was successful, false if no such lease exists + bool deleteLease6(const isc::asiolink::IOAddress& addr); + + /// @brief Returns backend name. + /// + /// Each backend have specific name, e.g. "mysql" or "sqlite". + std::string getName() const { return ("memfile"); } + + /// @brief Returns description of the backend. + /// + /// This description may be multiline text that describes the backend. + std::string getDescription() const; + + /// @brief Returns backend version. - std::string getVersion() const { return ("test-version"); } ++ virtual std::pair getVersion() const { ++ return (std::make_pair(1, 0)); ++ } ++ ++ /// @brief Commit Transactions ++ /// ++ /// Commits all pending database operations. On databases that don't ++ /// support transactions, this is a no-op. ++ virtual void commit(); ++ ++ /// @brief Rollback Transactions ++ /// ++ /// Rolls back all pending database operations. On databases that don't ++ /// support transactions, this is a no-op. ++ virtual void rollback(); + + using LeaseMgr::getParameter; + + protected: + + typedef boost::multi_index_container< // this is a multi-index container... + Lease6Ptr, // it will hold shared_ptr to leases6 + boost::multi_index::indexed_by< // and will be sorted by + // IPv6 address that are unique. That particular key is a member + // of the Lease6 structure, is of type IOAddress and can be accessed + // by doing &Lease6::addr_ + boost::multi_index::ordered_unique< + boost::multi_index::member + > + > + > Lease6Storage; // Let the whole contraption be called Lease6Storage. + + Lease6Storage storage6_; + }; + -}; // end of isc::dhcp::test namespace + }; // end of isc::dhcp namespace + }; // end of isc namespace + -#endif // MEMFILE_LEASE_MGR_H ++#endif // MEMFILE_LEASE_MGR_HSE4 ++ diff --cc src/lib/dhcp/tests/Makefile.am index 99548f63db,57055a7ec2..bf5e2e764e --- a/src/lib/dhcp/tests/Makefile.am +++ b/src/lib/dhcp/tests/Makefile.am @@@ -29,33 -26,30 +29,38 @@@ TESTS if HAVE_GTEST TESTS += libdhcp++_unittests libdhcpsrv_unittests libdhcp___unittests_SOURCES = run_unittests.cc -libdhcp___unittests_SOURCES += libdhcp++_unittest.cc libdhcp___unittests_SOURCES += iface_mgr_unittest.cc -libdhcp___unittests_SOURCES += option6_iaaddr_unittest.cc -libdhcp___unittests_SOURCES += option6_ia_unittest.cc -libdhcp___unittests_SOURCES += option6_addrlst_unittest.cc +libdhcp___unittests_SOURCES += libdhcp++_unittest.cc libdhcp___unittests_SOURCES += option4_addrlst_unittest.cc -libdhcp___unittests_SOURCES += option6_int_unittest.cc +libdhcp___unittests_SOURCES += option6_addrlst_unittest.cc - libdhcp___unittests_SOURCES += option6_iaaddr_unittest.cc +libdhcp___unittests_SOURCES += option6_ia_unittest.cc ++libdhcp___unittests_SOURCES += option6_iaaddr_unittest.cc + libdhcp___unittests_SOURCES += option6_int_array_unittest.cc -libdhcp___unittests_SOURCES += option_unittest.cc ++libdhcp___unittests_SOURCES += option6_int_unittest.cc + libdhcp___unittests_SOURCES += option_definition_unittest.cc -libdhcp___unittests_SOURCES += pkt6_unittest.cc +libdhcp___unittests_SOURCES += option_unittest.cc libdhcp___unittests_SOURCES += pkt4_unittest.cc -libdhcp___unittests_SOURCES += duid_unittest.cc +libdhcp___unittests_SOURCES += pkt6_unittest.cc +libdhcp___unittests_SOURCES += schema_copy.h libdhcp___unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES) libdhcp___unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) libdhcp___unittests_CXXFLAGS = $(AM_CXXFLAGS) libdhcpsrv_unittests_SOURCES = run_unittests.cc -libdhcpsrv_unittests_SOURCES += cfgmgr_unittest.cc triplet_unittest.cc -libdhcpsrv_unittests_SOURCES += pool_unittest.cc subnet_unittest.cc libdhcpsrv_unittests_SOURCES += addr_utilities_unittest.cc -libdhcpsrv_unittests_SOURCES += lease_mgr_unittest.cc + libdhcpsrv_unittests_SOURCES += alloc_engine_unittest.cc +libdhcpsrv_unittests_SOURCES += cfgmgr_unittest.cc +libdhcpsrv_unittests_SOURCES += duid_unittest.cc +libdhcpsrv_unittests_SOURCES += lease_mgr_factory_unittest.cc +libdhcpsrv_unittests_SOURCES += lease_mgr_unittest.cc ++libdhcpsrv_unittests_SOURCES += memfile_lease_mgr_unittest.cc +if HAVE_MYSQL +libdhcpsrv_unittests_SOURCES += mysql_lease_mgr_unittest.cc +endif +libdhcpsrv_unittests_SOURCES += pool_unittest.cc +libdhcpsrv_unittests_SOURCES += subnet_unittest.cc +libdhcpsrv_unittests_SOURCES += triplet_unittest.cc libdhcpsrv_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES) libdhcpsrv_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) diff --cc src/lib/dhcp/tests/alloc_engine_unittest.cc index 0000000000,91e08d802f..d4290a37e4 mode 000000,100644..100644 --- a/src/lib/dhcp/tests/alloc_engine_unittest.cc +++ b/src/lib/dhcp/tests/alloc_engine_unittest.cc @@@ -1,0 -1,343 +1,341 @@@ + // 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 + #include + #include + #include + + using namespace std; + using namespace isc; + using namespace isc::asiolink; + using namespace isc::dhcp; -using namespace isc::dhcp::test; // Memfile_LeaseMgr + + namespace { + + class NakedAllocEngine : public AllocEngine { + public: + NakedAllocEngine(AllocEngine::AllocType engine_type, unsigned int attempts) + :AllocEngine(engine_type, attempts) { + } + using AllocEngine::Allocator; + using AllocEngine::IterativeAllocator; + }; + + // empty class for now, but may be extended once Addr6 becomes bigger + class AllocEngineTest : public ::testing::Test { + public: + AllocEngineTest() { + duid_ = boost::shared_ptr(new DUID(vector(8, 0x42))); + iaid_ = 42; + + // instantiate cfg_mgr + CfgMgr& cfg_mgr = CfgMgr::instance(); + + subnet_ = Subnet6Ptr(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4)); + pool_ = Pool6Ptr(new Pool6(Pool6::TYPE_IA, IOAddress("2001:db8:1::10"), + IOAddress("2001:db8:1::20"))); + subnet_->addPool6(pool_); + cfg_mgr.addSubnet6(subnet_); + - leasemgr_ = new Memfile_LeaseMgr(""); ++ factory_.create("type=memfile"); + } + + void checkLease6(const Lease6Ptr& lease) { + // that is belongs to the right subnet + EXPECT_EQ(lease->subnet_id_, subnet_->getID()); + EXPECT_TRUE(subnet_->inRange(lease->addr_)); + EXPECT_TRUE(subnet_->inPool(lease->addr_)); + + // that it have proper parameters + EXPECT_EQ(iaid_, lease->iaid_); + EXPECT_EQ(subnet_->getValid(), lease->valid_lft_); + EXPECT_EQ(subnet_->getPreferred(), lease->preferred_lft_); + EXPECT_EQ(subnet_->getT1(), lease->t1_); + EXPECT_EQ(subnet_->getT2(), lease->t2_); + EXPECT_EQ(0, lease->prefixlen_); // this is IA_NA, not IA_PD + EXPECT_TRUE(false == lease->fqdn_fwd_); + EXPECT_TRUE(false == lease->fqdn_rev_); + EXPECT_TRUE(*lease->duid_ == *duid_); + // @todo: check cltt + } + + ~AllocEngineTest() { - LeaseMgr::instance().destroy_instance(); - leasemgr_ = NULL; ++ factory_.destroy(); + } + + DuidPtr duid_; + uint32_t iaid_; + Subnet6Ptr subnet_; + Pool6Ptr pool_; - LeaseMgr* leasemgr_; ++ LeaseMgrFactory factory_; + }; + + // This test checks if the Allocation Engine can be instantiated and that it + // parses parameters string properly. + TEST_F(AllocEngineTest, constructor) { + AllocEngine* x = NULL; + + // Hashed and random allocators are not supported yet + ASSERT_THROW(x = new AllocEngine(AllocEngine::ALLOC_HASHED, 5), NotImplemented); + ASSERT_THROW(x = new AllocEngine(AllocEngine::ALLOC_RANDOM, 5), NotImplemented); + + ASSERT_NO_THROW(x = new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)); + + delete x; + } + + /// @todo: This method is taken from mysql_lease_mgr_utilities.cc from ticket + /// #2342. Get rid of one instance once the code is merged + void + detailCompareLease6(const Lease6Ptr& first, const Lease6Ptr& second) { + EXPECT_EQ(first->type_, second->type_); + + // Compare address strings - odd things happen when they are different + // as the EXPECT_EQ appears to call the operator uint32_t() function, + // which causes an exception to be thrown for IPv6 addresses. + EXPECT_EQ(first->addr_.toText(), second->addr_.toText()); + EXPECT_EQ(first->prefixlen_, second->prefixlen_); + EXPECT_EQ(first->iaid_, second->iaid_); - EXPECT_TRUE(first->hwaddr_ == second->hwaddr_); + EXPECT_TRUE(*first->duid_ == *second->duid_); + EXPECT_EQ(first->preferred_lft_, second->preferred_lft_); + EXPECT_EQ(first->valid_lft_, second->valid_lft_); + EXPECT_EQ(first->cltt_, second->cltt_); + EXPECT_EQ(first->subnet_id_, second->subnet_id_); + } + + + // This test checks if the simple allocation can succeed + TEST_F(AllocEngineTest, simpleAlloc) { + boost::scoped_ptr engine; + ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100))); + ASSERT_TRUE(engine); + + Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_, IOAddress("::"), + false); + + // check that we got a lease + ASSERT_TRUE(lease); + + // do all checks on the lease + checkLease6(lease); + + // Check that the lease is indeed in LeaseMgr - Lease6Ptr from_mgr = LeaseMgr::instance().getLease6(lease->addr_); ++ Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease->addr_); + ASSERT_TRUE(from_mgr); + + // Now check that the lease in LeaseMgr has the same parameters + detailCompareLease6(lease, from_mgr); + } + + // This test checks if the fake allocation (for SOLICIT) can succeed + TEST_F(AllocEngineTest, fakeAlloc) { + boost::scoped_ptr engine; + ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100))); + ASSERT_TRUE(engine); + + Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_, IOAddress("::"), + true); + + // check that we got a lease + ASSERT_TRUE(lease); + + // do all checks on the lease + checkLease6(lease); + + // Check that the lease is NOT in LeaseMgr - Lease6Ptr from_mgr = LeaseMgr::instance().getLease6(lease->addr_); ++ Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease->addr_); + ASSERT_FALSE(from_mgr); + } + + // This test checks if the allocation with a hint that is valid (in range, + // in pool and free) can succeed + TEST_F(AllocEngineTest, allocWithValidHint) { + boost::scoped_ptr engine; + ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100))); + ASSERT_TRUE(engine); + + Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_, + IOAddress("2001:db8:1::15"), + false); + + // check that we got a lease + ASSERT_TRUE(lease); + + // we should get what we asked for + EXPECT_EQ(lease->addr_.toText(), "2001:db8:1::15"); + + // do all checks on the lease + checkLease6(lease); + + // Check that the lease is indeed in LeaseMgr - Lease6Ptr from_mgr = LeaseMgr::instance().getLease6(lease->addr_); ++ Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease->addr_); + ASSERT_TRUE(from_mgr); + + // Now check that the lease in LeaseMgr has the same parameters + detailCompareLease6(lease, from_mgr); + } + + // This test checks if the allocation with a hint that is in range, + // in pool, but is currently used) can succeed + TEST_F(AllocEngineTest, allocWithUsedHint) { + boost::scoped_ptr engine; + ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100))); + ASSERT_TRUE(engine); + + // let's create a lease and put it in the LeaseMgr + DuidPtr duid2 = boost::shared_ptr(new DUID(vector(8, 0xff))); + Lease6Ptr used(new Lease6(Lease6::LEASE_IA_NA, IOAddress("2001:db8:1::1f"), + duid2, 1, 2, 3, 4, 5, subnet_->getID())); - ASSERT_TRUE(LeaseMgr::instance().addLease(used)); ++ ASSERT_TRUE(LeaseMgrFactory::instance().addLease(used)); + + // another client comes in and request an address that is in pool, but + // unfortunately it is used already. The same address must not be allocated + // twice. + Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_, + IOAddress("2001:db8:1::1f"), + false); + // check that we got a lease + ASSERT_TRUE(lease); + + // allocated address must be different + EXPECT_TRUE(used->addr_.toText() != lease->addr_.toText()); + + // we should NOT get what we asked for, because it is used already + EXPECT_TRUE(lease->addr_.toText() != "2001:db8:1::1f"); + + // do all checks on the lease + checkLease6(lease); + + // Check that the lease is indeed in LeaseMgr - Lease6Ptr from_mgr = LeaseMgr::instance().getLease6(lease->addr_); ++ Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease->addr_); + ASSERT_TRUE(from_mgr); + + // Now check that the lease in LeaseMgr has the same parameters + detailCompareLease6(lease, from_mgr); + } + + // This test checks if the allocation with a hint that is out the blue + // can succeed. The invalid hint should be ignored completely. + TEST_F(AllocEngineTest, allocBogusHint) { + boost::scoped_ptr engine; + ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100))); + ASSERT_TRUE(engine); + + // Client would like to get a 3000::abc lease, which does not belong to any + // supported lease. Allocation engine should ignore it and carry on + // with the normal allocation + Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_, + IOAddress("3000::abc"), + false); + // check that we got a lease + ASSERT_TRUE(lease); + + // we should NOT get what we asked for, because it is used already + EXPECT_TRUE(lease->addr_.toText() != "3000::abc"); + + // do all checks on the lease + checkLease6(lease); + + // Check that the lease is indeed in LeaseMgr - Lease6Ptr from_mgr = LeaseMgr::instance().getLease6(lease->addr_); ++ Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease->addr_); + ASSERT_TRUE(from_mgr); + + // Now check that the lease in LeaseMgr has the same parameters + detailCompareLease6(lease, from_mgr); + } + + // This test verifies that the allocator picks addresses that belong to the + // pool + TEST_F(AllocEngineTest, IterativeAllocator) { + NakedAllocEngine::Allocator* alloc = new NakedAllocEngine::IterativeAllocator(); + + for (int i = 0; i < 1000; ++i) { + IOAddress candidate = alloc->pickAddress(subnet_, duid_, IOAddress("::")); + + EXPECT_TRUE(subnet_->inPool(candidate)); + } + + delete alloc; + } + + + // This test verifies that the iterative allocator really walks over all addresses + // in all pools in specified subnet. It also must not pick the same address twice + // unless it runs out of pool space and must start over. + TEST_F(AllocEngineTest, IterativeAllocator_manyPools) { + NakedAllocEngine::IterativeAllocator* alloc = new NakedAllocEngine::IterativeAllocator(); + + // let's start from 2, as there is 2001:db8:1::10 - 2001:db8:1::20 pool already. + for (int i = 2; i < 10; ++i) { + stringstream min, max; + + min << "2001:db8:1::" << hex << i*16 + 1; + max << "2001:db8:1::" << hex << i*16 + 9; + + Pool6Ptr pool(new Pool6(Pool6::TYPE_IA, IOAddress(min.str()), + IOAddress(max.str()))); + // cout << "Adding pool: " << min.str() << "-" << max.str() << endl; + subnet_->addPool6(pool); + } + + int total = 17 + 8*9; // first pool (::10 - ::20) has 17 addresses in it, + // there are 8 extra pools with 9 addresses in each. + + // Let's keep picked addresses here and check their uniqueness. + std::map generated_addrs; + int cnt = 0; + while (++cnt) { + IOAddress candidate = alloc->pickAddress(subnet_, duid_, IOAddress("::")); + EXPECT_TRUE(subnet_->inPool(candidate)); + + // One way to easily verify that the iterative allocator really works is + // to uncomment the following line and observe its output that it + // covers all defined subnets. + // cout << candidate.toText() << endl; + + if (generated_addrs.find(candidate) == generated_addrs.end()) { + // we haven't had this + generated_addrs[candidate] = 0; + } else { + // we have seen this address before. That should mean that we + // iterated over all addresses. + if (generated_addrs.size() == total) { + // we have exactly the number of address in all pools + break; + } + ADD_FAILURE() << "Too many or not enough unique addresses generated."; + break; + } + + if ( cnt>total ) { + ADD_FAILURE() << "Too many unique addresses generated."; + break; + } + } + + delete alloc; + } + + }; // end of anonymous namespace diff --cc src/lib/dhcp/tests/lease_mgr_unittest.cc index 897301a7fa,66521bae61..64ce9b1653 --- a/src/lib/dhcp/tests/lease_mgr_unittest.cc +++ b/src/lib/dhcp/tests/lease_mgr_unittest.cc @@@ -24,281 -25,167 +24,257 @@@ using namespace std using namespace isc; using namespace isc::asiolink; using namespace isc::dhcp; -using namespace isc::dhcp::test; // Memfile_LeaseMgr - // This is a concrete implementation of a Lease database. - // It does not do anything useful now, and is used for abstract LeaseMgr - // class testing. It may later evolve into more useful backend if the - // need arises. We can reuse code from memfile benchmark. See code in - // tests/tools/dhcp-ubench/memfile_bench.{cc|h} - class Memfile_LeaseMgr : public LeaseMgr { -namespace { -// empty class for now, but may be extended once Addr6 becomes bigger -class LeaseMgrTest : public ::testing::Test { ++// This is a concrete implementation of a Lease database. It does not do ++// anything useful and is used for abstract LeaseMgr class testing. ++class ConcreteLeaseMgr : public LeaseMgr { public: - LeaseMgrTest() { - } -}; -// This test checks if the LeaseMgr can be instantiated and that it -// parses parameters string properly. -TEST_F(LeaseMgrTest, constructor) { - - // should not throw any exceptions here - Memfile_LeaseMgr * leaseMgr = new Memfile_LeaseMgr(""); - delete leaseMgr; - - leaseMgr = new Memfile_LeaseMgr("param1=value1 param2=value2"); - - EXPECT_EQ("value1", leaseMgr->getParameter("param1")); - EXPECT_EQ("value2", leaseMgr->getParameter("param2")); - EXPECT_THROW(leaseMgr->getParameter("param3"), BadValue); + /// @brief The sole lease manager constructor + /// + /// dbconfig is a generic way of passing parameters. Parameters + /// are passed in the "name=value" format, separated by spaces. + /// Values may be enclosed in double quotes, if needed. + /// + /// @param parameters A data structure relating keywords and values + /// concerned with the database. - Memfile_LeaseMgr(const LeaseMgr::ParameterMap& parameters); ++ ConcreteLeaseMgr(const LeaseMgr::ParameterMap& parameters) ++ : LeaseMgr(parameters) ++ {} + - /// @brief Destructor (closes file) - virtual ~Memfile_LeaseMgr(); ++ /// @brief Destructor ++ virtual ~ConcreteLeaseMgr() ++ {} + + /// @brief Adds an IPv4 lease. + /// + /// @param lease lease to be added - virtual bool addLease(const Lease4Ptr& lease); ++ virtual bool addLease(const Lease4Ptr&) { ++ return (false); ++ } - delete leaseMgr; -} + /// @brief Adds an IPv6 lease. + /// + /// @param lease lease to be added - virtual bool addLease(const Lease6Ptr& lease); ++ virtual bool addLease(const Lease6Ptr&) { ++ return (false); ++ } -// There's no point in calling any other methods in LeaseMgr, as they -// are purely virtual, so we would only call Memfile_LeaseMgr methods. -// Those methods are just stubs that does not return anything. -// It seems likely that we will need to extend the memfile code for -// allocation engine tests, so we may implement tests that call -// Memfile_LeaseMgr methods then. + /// @brief Returns existing IPv4 lease for specified IPv4 address. + /// + /// @param addr address of the searched lease + /// - /// @return a collection of leases - virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress& addr) const; ++ /// @return smart pointer to the lease (or NULL if a lease is not found) ++ virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress&) const { ++ return (Lease4Ptr()); ++ } -TEST_F(LeaseMgrTest, addGetDelete) { - Memfile_LeaseMgr * leaseMgr = new Memfile_LeaseMgr(""); + /// @brief Returns existing IPv4 lease for specific address and subnet + /// @param addr address of the searched lease + /// @param subnet_id ID of the subnet the lease must belong to + /// + /// @return smart pointer to the lease (or NULL if a lease is not found) - virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress& addr, - SubnetID subnet_id) const; ++ virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress&, ++ SubnetID) const { ++ return (Lease4Ptr()); ++ } - IOAddress addr("2001:db8:1::456"); + /// @brief Returns existing IPv4 leases for specified hardware address. + /// + /// Although in the usual case there will be only one lease, for mobile + /// clients or clients with multiple static/fixed/reserved leases there + /// can be more than one. Thus return type is a container, not a single + /// pointer. + /// + /// @param hwaddr hardware address of the client + /// + /// @return lease collection - virtual Lease4Collection getLease4(const HWAddr& hwaddr) const; ++ virtual Lease4Collection getLease4(const HWAddr&) const { ++ return (Lease4Collection()); ++ } - uint8_t llt[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}; - DuidPtr duid(new DUID(llt, sizeof(llt))); + /// @brief Returns existing IPv4 leases for specified hardware address + /// and a subnet + /// + /// There can be at most one lease for a given HW address in a single + /// pool, so this method with either return a single lease or NULL. + /// + /// @param hwaddr hardware address of the client + /// @param subnet_id identifier of the subnet that lease must belong to + /// + /// @return a pointer to the lease (or NULL if a lease is not found) - virtual Lease4Ptr getLease4(const HWAddr& hwaddr, - SubnetID subnet_id) const; ++ virtual Lease4Ptr getLease4(const HWAddr&, SubnetID) const { ++ return (Lease4Ptr()); ++ } - uint32_t iaid = 7; // just a number + /// @brief Returns existing IPv4 lease for specified client-id + /// + /// @param clientid client identifier - virtual Lease4Collection getLease4(const ClientId& clientid) const; ++ /// ++ /// @return lease collection ++ virtual Lease4Collection getLease4(const ClientId&) const { ++ return (Lease4Collection()); ++ } - SubnetID subnet_id = 8; // just another number + /// @brief Returns existing IPv4 lease for specified client-id + /// + /// There can be at most one lease for a given HW address in a single + /// pool, so this method with either return a single lease or NULL. + /// + /// @param clientid client identifier + /// @param subnet_id identifier of the subnet that lease must belong to + /// + /// @return a pointer to the lease (or NULL if a lease is not found) - virtual Lease4Ptr getLease4(const ClientId& clientid, - SubnetID subnet_id) const; ++ virtual Lease4Ptr getLease4(const ClientId&, SubnetID) const { ++ return (Lease4Ptr()); ++ } - Lease6Ptr lease(new Lease6(Lease6::LEASE_IA_NA, addr, - duid, iaid, 100, 200, 50, 80, - subnet_id)); + /// @brief Returns existing IPv6 lease for a given IPv6 address. + /// + /// @param addr address of the searched lease + /// + /// @return smart pointer to the lease (or NULL if a lease is not found) - Lease6Ptr getLease6(const isc::asiolink::IOAddress& addr) const; ++ Lease6Ptr getLease6(const isc::asiolink::IOAddress&) const { ++ return (Lease6Ptr()); ++ } - EXPECT_TRUE(leaseMgr->addLease(lease)); + /// @brief Returns existing IPv6 lease for a given DUID+IA combination + /// + /// @param duid client DUID + /// @param iaid IA identifier + /// + /// @return collection of IPv6 leases - Lease6Collection getLease6(const DUID& duid, uint32_t iaid) const; ++ Lease6Collection getLease6(const DUID&, uint32_t) const { ++ return (Lease6Collection()); ++ } - // should not be allowed to add a second lease with the same address - EXPECT_FALSE(leaseMgr->addLease(lease)); + /// @brief Returns existing IPv6 lease for a given DUID+IA combination + /// + /// @param duid client DUID + /// @param iaid IA identifier + /// @param subnet_id identifier of the subnet the lease must belong to + /// + /// @return smart pointer to the lease (or NULL if a lease is not found) - Lease6Ptr getLease6(const DUID& duid, uint32_t iaid, SubnetID subnet_id) const; ++ Lease6Ptr getLease6(const DUID&, uint32_t, SubnetID) const { ++ return (Lease6Ptr()); ++ } - Lease6Ptr x = leaseMgr->getLease6(IOAddress("2001:db8:1::234")); - EXPECT_EQ(Lease6Ptr(), x); + /// @brief Updates IPv4 lease. + /// + /// @param lease4 The lease to be updated. + /// + /// If no such lease is present, an exception will be thrown. - void updateLease4(const Lease4Ptr& lease4); ++ void updateLease4(const Lease4Ptr&) {} + + /// @brief Updates IPv4 lease. + /// + /// @param lease4 The lease to be updated. + /// + /// If no such lease is present, an exception will be thrown. - void updateLease6(const Lease6Ptr& lease6); ++ void updateLease6(const Lease6Ptr&) {} + + /// @brief Deletes a lease. + /// + /// @param addr IPv4 address of the lease to be deleted. + /// + /// @return true if deletion was successful, false if no such lease exists - bool deleteLease4(const isc::asiolink::IOAddress& addr); ++ bool deleteLease4(const isc::asiolink::IOAddress&) { ++ return (false); ++ } - x = leaseMgr->getLease6(IOAddress("2001:db8:1::456")); - ASSERT_TRUE(x); + /// @brief Deletes a lease. + /// + /// @param addr IPv4 address of the lease to be deleted. + /// + /// @return true if deletion was successful, false if no such lease exists - bool deleteLease6(const isc::asiolink::IOAddress& addr); ++ bool deleteLease6(const isc::asiolink::IOAddress&) { ++ return (false); ++ } - EXPECT_EQ(x->addr_.toText(), addr.toText()); - EXPECT_TRUE(*x->duid_ == *duid); - EXPECT_EQ(x->iaid_, iaid); - EXPECT_EQ(x->subnet_id_, subnet_id); - - // These are not important from lease management perspective, but - // let's check them anyway. - EXPECT_EQ(x->type_, Lease6::LEASE_IA_NA); - EXPECT_EQ(x->preferred_lft_, 100); - EXPECT_EQ(x->valid_lft_, 200); - EXPECT_EQ(x->t1_, 50); - EXPECT_EQ(x->t2_, 80); - - // Test getLease6(duid, iaid, subnet_id) - positive case - Lease6Ptr y = leaseMgr->getLease6(*duid, iaid, subnet_id); - ASSERT_TRUE(y); - EXPECT_TRUE(*y->duid_ == *duid); - EXPECT_EQ(y->iaid_, iaid); - EXPECT_EQ(y->addr_.toText(), addr.toText()); - - // Test getLease6(duid, iaid, subnet_id) - wrong iaid - uint32_t invalid_iaid = 9; // no such iaid - y = leaseMgr->getLease6(*duid, invalid_iaid, subnet_id); - EXPECT_FALSE(y); - - uint32_t invalid_subnet_id = 999; - y = leaseMgr->getLease6(*duid, iaid, invalid_subnet_id); - EXPECT_FALSE(y); - - // truncated duid - DuidPtr invalid_duid(new DUID(llt, sizeof(llt) - 1)); - y = leaseMgr->getLease6(*invalid_duid, iaid, subnet_id); - EXPECT_FALSE(y); - - // should return false - there's no such address - EXPECT_FALSE(leaseMgr->deleteLease6(IOAddress("2001:db8:1::789"))); - - // this one should succeed - EXPECT_TRUE(leaseMgr->deleteLease6(IOAddress("2001:db8:1::456"))); - - // after the lease is deleted, it should really be gone - x = leaseMgr->getLease6(IOAddress("2001:db8:1::456")); - EXPECT_EQ(Lease6Ptr(), x); - - delete leaseMgr; -} + /// @brief Returns backend name. + /// + /// Each backend have specific name, e.g. "mysql" or "sqlite". - std::string getName() const { return "memfile"; } ++ std::string getName() const { ++ return (std::string("concrete")); ++ } -// This test checks there that leaseMgr is really a singleton and that -// no more than one can be created. -TEST_F(LeaseMgrTest, singleton) { - Memfile_LeaseMgr* leaseMgr1 = NULL; - Memfile_LeaseMgr* leaseMgr2 = NULL; + /// @brief Returns description of the backend. + /// + /// This description may be multiline text that describes the backend. - std::string getDescription() const; ++ std::string getDescription() const { ++ return (std::string("This is a dummy concrete backend implementation.")); ++ } - EXPECT_THROW(LeaseMgr::instance(), InvalidOperation); + /// @brief Returns backend version. + std::pair getVersion() const { + return (make_pair(uint32_t(0), uint32_t(0))); + } - EXPECT_NO_THROW( leaseMgr1 = new Memfile_LeaseMgr("") ); + /// @brief Commit transactions + void commit() { + } - EXPECT_NO_THROW(LeaseMgr::instance()); + /// @brief Rollback transactions + void rollback() { + } - - using LeaseMgr::getParameter; - - protected: - - +}; - Memfile_LeaseMgr::Memfile_LeaseMgr(const LeaseMgr::ParameterMap& parameters) - : LeaseMgr(parameters) { - } - - Memfile_LeaseMgr::~Memfile_LeaseMgr() { - } - - bool Memfile_LeaseMgr::addLease(const boost::shared_ptr&) { - return (false); - } - - bool Memfile_LeaseMgr::addLease(const boost::shared_ptr&) { - return (false); - } - - Lease4Ptr Memfile_LeaseMgr::getLease4(const isc::asiolink::IOAddress&) const { - return (Lease4Ptr()); - } - - Lease4Collection Memfile_LeaseMgr::getLease4(const HWAddr& ) const { - return (Lease4Collection()); - } - - Lease4Ptr Memfile_LeaseMgr::getLease4(const isc::asiolink::IOAddress & , - SubnetID) const { - return (Lease4Ptr()); - } - - Lease4Ptr Memfile_LeaseMgr::getLease4(const HWAddr&, - SubnetID) const { - return (Lease4Ptr()); - } - - - Lease4Ptr Memfile_LeaseMgr::getLease4(const ClientId&, - SubnetID) const { - return (Lease4Ptr()); - } - - Lease4Collection Memfile_LeaseMgr::getLease4(const ClientId& ) const { - return (Lease4Collection()); - } - - Lease6Ptr Memfile_LeaseMgr::getLease6(const isc::asiolink::IOAddress&) const { - return (Lease6Ptr()); - } - - Lease6Collection Memfile_LeaseMgr::getLease6(const DUID& , uint32_t ) const { - return (Lease6Collection()); - } - - Lease6Ptr Memfile_LeaseMgr::getLease6(const DUID&, uint32_t, - SubnetID) const { - return (Lease6Ptr()); - } - - void Memfile_LeaseMgr::updateLease4(const Lease4Ptr&) { - } - - void Memfile_LeaseMgr::updateLease6(const Lease6Ptr&) { - - } - - bool Memfile_LeaseMgr::deleteLease4(const isc::asiolink::IOAddress&) { - return (false); - } - - bool Memfile_LeaseMgr::deleteLease6(const isc::asiolink::IOAddress&) { - return (false); - } - - std::string Memfile_LeaseMgr::getDescription() const { - return (string("This is a dummy memfile backend implementation.\n" - "It does not offer any useful lease management and its only\n" - "purpose is to test abstract lease manager API.")); - } - - // There can be only one instance of any LeaseMgr derived - // objects instantiated at any time. - ASSERT_THROW(leaseMgr2 = new Memfile_LeaseMgr(""), InvalidOperation); +namespace { +// empty class for now, but may be extended once Addr6 becomes bigger +class LeaseMgrTest : public ::testing::Test { +public: + LeaseMgrTest() { + } +}; - delete leaseMgr1; +// This test checks if the LeaseMgr can be instantiated and that it +// parses parameters string properly. +TEST_F(LeaseMgrTest, getParameter) { - ASSERT_NO_THROW(leaseMgr2 = new Memfile_LeaseMgr("") ); + LeaseMgr::ParameterMap pmap; + pmap[std::string("param1")] = std::string("value1"); + pmap[std::string("param2")] = std::string("value2"); - Memfile_LeaseMgr leasemgr(pmap); ++ ConcreteLeaseMgr leasemgr(pmap); - delete leaseMgr2; + EXPECT_EQ("value1", leasemgr.getParameter("param1")); + EXPECT_EQ("value2", leasemgr.getParameter("param2")); + EXPECT_THROW(leasemgr.getParameter("param3"), BadValue); } +// There's no point in calling any other methods in LeaseMgr, as they - // are purely virtual, so we would only call Memfile_LeaseMgr methods. - // Those methods are just stubs that does not return anything. - // It seems likely that we will need to extend the memfile code for - // allocation engine tests, so we may implement tests that call - // Memfile_LeaseMgr methods then. ++// are purely virtual, so we would only call ConcreteLeaseMgr methods. ++// Those methods are just stubs that do not return anything. + ++// Lease6 is also defined in lease_mgr.h, so is tested in this file as well. + // This test checks if the Lease6 structure can be instantiated correctly -TEST(Lease6, ctor) { ++TEST(Lease6, Lease6Constructor) { + + IOAddress addr("2001:db8:1::456"); + + uint8_t llt[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}; + DuidPtr duid(new DUID(llt, sizeof(llt))); + + uint32_t iaid = 7; // just a number + + SubnetID subnet_id = 8; // just another number + + Lease6Ptr x(new Lease6(Lease6::LEASE_IA_NA, addr, + duid, iaid, 100, 200, 50, 80, + subnet_id)); + + EXPECT_TRUE(x->addr_ == addr); + EXPECT_TRUE(*x->duid_ == *duid); + EXPECT_TRUE(x->iaid_ == iaid); + EXPECT_TRUE(x->subnet_id_ == subnet_id); + EXPECT_TRUE(x->type_ == Lease6::LEASE_IA_NA); + EXPECT_TRUE(x->preferred_lft_ == 100); + EXPECT_TRUE(x->valid_lft_ == 200); + EXPECT_TRUE(x->t1_ == 50); + EXPECT_TRUE(x->t2_ == 80); + + // Lease6 must be instantiated with a DUID, not with NULL pointer + EXPECT_THROW(new Lease6(Lease6::LEASE_IA_NA, addr, + DuidPtr(), iaid, 100, 200, 50, 80, + subnet_id), InvalidOperation); + } - }; // end of anonymous namespace diff --cc src/lib/dhcp/tests/memfile_lease_mgr_unittest.cc index 0000000000,0000000000..c625a16f28 new file mode 100644 --- /dev/null +++ b/src/lib/dhcp/tests/memfile_lease_mgr_unittest.cc @@@ -1,0 -1,0 +1,130 @@@ ++// 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 ++ ++using namespace std; ++using namespace isc; ++using namespace isc::asiolink; ++using namespace isc::dhcp; ++ ++namespace { ++// empty class for now, but may be extended once Addr6 becomes bigger ++class MemfileLeaseMgrTest : public ::testing::Test { ++public: ++ MemfileLeaseMgrTest() { ++ } ++}; ++ ++// This test checks if the LeaseMgr can be instantiated and that it ++// parses parameters string properly. ++TEST_F(MemfileLeaseMgrTest, constructor) { ++ ++ const LeaseMgr::ParameterMap pmap; // Empty parameter map ++ boost::scoped_ptr lease_mgr; ++ ++ ASSERT_NO_THROW(lease_mgr.reset(new Memfile_LeaseMgr(pmap))); ++} ++ ++// There's no point in calling any other methods in LeaseMgr, as they ++// are purely virtual, so we would only call Memfile_LeaseMgr methods. ++// Those methods are just stubs that does not return anything. ++// It seems likely that we will need to extend the memfile code for ++// allocation engine tests, so we may implement tests that call ++// Memfile_LeaseMgr methods then. ++ ++TEST_F(MemfileLeaseMgrTest, addGetDelete) { ++ const LeaseMgr::ParameterMap pmap; // Empty parameter map ++ boost::scoped_ptr lease_mgr(new Memfile_LeaseMgr(pmap)); ++ ++ IOAddress addr("2001:db8:1::456"); ++ ++ uint8_t llt[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}; ++ DuidPtr duid(new DUID(llt, sizeof(llt))); ++ ++ uint32_t iaid = 7; // just a number ++ ++ SubnetID subnet_id = 8; // just another number ++ ++ Lease6Ptr lease(new Lease6(Lease6::LEASE_IA_NA, addr, ++ duid, iaid, 100, 200, 50, 80, ++ subnet_id)); ++ ++ EXPECT_TRUE(lease_mgr->addLease(lease)); ++ ++ // should not be allowed to add a second lease with the same address ++ EXPECT_FALSE(lease_mgr->addLease(lease)); ++ ++ Lease6Ptr x = lease_mgr->getLease6(IOAddress("2001:db8:1::234")); ++ EXPECT_EQ(Lease6Ptr(), x); ++ ++ x = lease_mgr->getLease6(IOAddress("2001:db8:1::456")); ++ ASSERT_TRUE(x); ++ ++ EXPECT_EQ(x->addr_.toText(), addr.toText()); ++ EXPECT_TRUE(*x->duid_ == *duid); ++ EXPECT_EQ(x->iaid_, iaid); ++ EXPECT_EQ(x->subnet_id_, subnet_id); ++ ++ // These are not important from lease management perspective, but ++ // let's check them anyway. ++ EXPECT_EQ(x->type_, Lease6::LEASE_IA_NA); ++ EXPECT_EQ(x->preferred_lft_, 100); ++ EXPECT_EQ(x->valid_lft_, 200); ++ EXPECT_EQ(x->t1_, 50); ++ EXPECT_EQ(x->t2_, 80); ++ ++ // Test getLease6(duid, iaid, subnet_id) - positive case ++ Lease6Ptr y = lease_mgr->getLease6(*duid, iaid, subnet_id); ++ ASSERT_TRUE(y); ++ EXPECT_TRUE(*y->duid_ == *duid); ++ EXPECT_EQ(y->iaid_, iaid); ++ EXPECT_EQ(y->addr_.toText(), addr.toText()); ++ ++ // Test getLease6(duid, iaid, subnet_id) - wrong iaid ++ uint32_t invalid_iaid = 9; // no such iaid ++ y = lease_mgr->getLease6(*duid, invalid_iaid, subnet_id); ++ EXPECT_FALSE(y); ++ ++ uint32_t invalid_subnet_id = 999; ++ y = lease_mgr->getLease6(*duid, iaid, invalid_subnet_id); ++ EXPECT_FALSE(y); ++ ++ // truncated duid ++ DuidPtr invalid_duid(new DUID(llt, sizeof(llt) - 1)); ++ y = lease_mgr->getLease6(*invalid_duid, iaid, subnet_id); ++ EXPECT_FALSE(y); ++ ++ // should return false - there's no such address ++ EXPECT_FALSE(lease_mgr->deleteLease6(IOAddress("2001:db8:1::789"))); ++ ++ // this one should succeed ++ EXPECT_TRUE(lease_mgr->deleteLease6(IOAddress("2001:db8:1::456"))); ++ ++ // after the lease is deleted, it should really be gone ++ x = lease_mgr->getLease6(IOAddress("2001:db8:1::456")); ++ EXPECT_EQ(Lease6Ptr(), x); ++} ++ ++// TODO: Write more memfile tests ++ ++}; // end of anonymous namespace