* - @subpage libdhcp
* - @subpage libdhcpIntro
* - @subpage libdhcpIfaceMgr
+ * - @subpage libdhcpsrv
+ * - @subpage leasemgr
+ * - @subpage cfgmgr
+ * - @subpage allocengine
+ * - @subpage dhcp-database-backends
* - @subpage perfdhcpInternals
*
* @section misc Miscellaneous topics
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
++#include <config.h>
++
#include <stdlib.h>
#include <time.h>
#include <exceptions/exceptions.h>
#include <util/io_utilities.h>
#include <util/range_utilities.h>
+ #include <dhcp/duid.h>
+ #include <dhcp/lease_mgr.h>
++#include <dhcp/lease_mgr_factory.h>
+ #include <dhcp/cfgmgr.h>
+ #include <dhcp/option6_iaaddr.h>
+
+ // @todo: Replace this with MySQL_LeaseMgr (or a LeaseMgr factory)
+ // once it is merged
+ #include <dhcp/memfile_lease_mgr.h>
+
+ #include <boost/foreach.hpp>
using namespace isc;
using namespace isc::asiolink;
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() {
// PERFORMANCE OF THIS SOFTWARE.
#include <config.h>
- #include <iostream>
+
#include <fstream>
+ #include <iostream>
#include <sstream>
- #include <arpa/inet.h>
#include <gtest/gtest.h>
+ #include <asiolink/io_address.h>
+ #include <boost/scoped_ptr.hpp>
+ #include <config/ccsession.h>
+ #include <dhcp/cfgmgr.h>
#include <dhcp/dhcp6.h>
+ #include <dhcp/duid.h>
+ #include <dhcp/lease_mgr.h>
++#include <dhcp/lease_mgr_factory.h>
+ #include <dhcp/option.h>
+ #include <dhcp/option6_addrlst.h>
#include <dhcp/option6_ia.h>
+ #include <dhcp/option6_iaaddr.h>
+ #include <dhcp/option6_int_array.h>
+ #include <dhcp6/config_parser.h>
#include <dhcp6/dhcp6_srv.h>
#include <util/buffer.h>
#include <util/range_utilities.h>
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<Option6IA> generateIA(uint32_t iaid, uint32_t t1, uint32_t t2) {
+ boost::shared_ptr<Option6IA> ia =
+ boost::shared_ptr<Option6IA>(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<Option6IAAddr> 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<Option6IAAddr>());
+ }
+
+ boost::shared_ptr<Option6IA> ia = boost::dynamic_pointer_cast<Option6IA>(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<Option6IAAddr> addr = boost::dynamic_pointer_cast<Option6IAAddr>(tmp);
+ return (addr);
+ }
+
+ // Check that generated IAADDR option contains expected address.
+ void checkIAAddr(const boost::shared_ptr<Option6IAAddr>& 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<Option6IAAddr> addr) {
+ boost::shared_ptr<Option6IA> ia = boost::dynamic_pointer_cast<Option6IA>(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
}
}
- 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<NakedDhcpv6Srv> 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<Pkt6> 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<Option6IntArray<uint16_t> >
+ option_oro(new Option6IntArray<uint16_t>(D6O_ORO));
+ // Create vector with two option codes.
+ std::vector<uint16_t> 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<Option6AddrLst> reply_nameservers =
+ boost::dynamic_pointer_cast<Option6AddrLst>(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<uint8_t> 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<NakedDhcpv6Srv> 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<Option6IA> ia =
- boost::shared_ptr<Option6IA>(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<Option6IAAddr> 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<NakedDhcpv6Srv> 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<Option6IA> 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<Option6IAAddr> 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<Option6IAAddr> 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<NakedDhcpv6Srv> 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<Option6IA> 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<Pkt6> 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<Option6IAAddr> 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<NakedDhcpv6Srv> 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<Pkt6>() );
+ 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<Option6IAAddr> addr1 = checkIA_NA(reply1, 1, subnet_->getT1(),
+ subnet_->getT2());
+ boost::shared_ptr<Option6IAAddr> addr2 = checkIA_NA(reply2, 2, subnet_->getT1(),
+ subnet_->getT2());
+ boost::shared_ptr<Option6IAAddr> 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<NakedDhcpv6Srv> 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<Option6IA> 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<Option6IA*>(tmp.get());
- EXPECT_EQ( 234, reply_ia->getIAID() );
+ // check that IA_NA was returned and that there's an address included
+ boost::shared_ptr<Option6IAAddr> 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<NakedDhcpv6Srv> 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<Option6IAAddr> addr1 = checkIA_NA(reply1, 1, subnet_->getT1(),
+ subnet_->getT2());
+ boost::shared_ptr<Option6IAAddr> addr2 = checkIA_NA(reply2, 2, subnet_->getT1(),
+ subnet_->getT2());
+ boost::shared_ptr<Option6IAAddr> 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) {
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 += duid.cc duid.h
+ libb10_dhcp___la_SOURCES += pkt4.cc pkt4.h
libb10_dhcp___la_CXXFLAGS = $(AM_CXXFLAGS)
libb10_dhcp___la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
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
// 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 <asiolink/io_address.h>
namespace isc {
};
};
++
++#endif // ADDR_UTILITIES_H
--- /dev/null
- Lease6Ptr existing = LeaseMgr::instance().getLease6(*duid, iaid, subnet->getID());
+ // 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 <alloc_engine.h>
++#include <lease_mgr_factory.h>
+ #include <string.h>
+
+ #include <cstring>
+
+ 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<Allocator>(new IterativeAllocator());
+ break;
+ case ALLOC_HASHED:
+ allocator_ = boost::shared_ptr<Allocator>(new HashedAllocator());
+ break;
+ case ALLOC_RANDOM:
+ allocator_ = boost::shared_ptr<Allocator>(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.
- existing = LeaseMgr::instance().getLease6(hint);
++ 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)) {
- Lease6Ptr existing = LeaseMgr::instance().getLease6(candidate);
++ 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
+
- bool status = LeaseMgr::instance().addLease(lease);
++ 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
- Lease6Ptr existing = LeaseMgr::instance().getLease6(addr);
++ 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 = 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
} // namespace isc::dhcp
} // namespace isc
--#endif
++#endif // CFGMGR_H
#define IRT_DEFAULT 86400
#define IRT_MINIMUM 600
--#endif
++#endif /* DHCP6_H */
}; // namespace isc::dhcp
}; // namespace isc
--#endif
++#endif // IFACE_MGR_H
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
-#include "lease_mgr.h"
+#include <dhcp/lease_mgr.h>
+ #include <exceptions/exceptions.h>
++
+ #include <boost/foreach.hpp>
+ #include <boost/algorithm/string.hpp>
-#include <sstream>
++
++#include <algorithm>
+ #include <iostream>
++#include <iterator>
+ #include <map>
-#include <iostream>
-#include <string>
+ #include <sstream>
-#include <algorithm>
-#include <iterator>
++#include <string>
++
+ #include <time.h>
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<int>(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<int>(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<string> 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<string,string>(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<std::string, std::string>::const_iterator param
- = parameters_.find(name);
- if (param == parameters_.end()) {
- isc_throw(BadValue, "Parameter not found");
- }
- return (param->second);
+
- instance_ = NULL;
++LeaseMgr::LeaseMgr(const LeaseMgr::ParameterMap& parameters)
++ : parameters_(parameters) {
+ }
+
+ LeaseMgr::~LeaseMgr() {
+ }
#ifndef LEASE_MGR_H
#define LEASE_MGR_H
-#include <string>
-#include <fstream>
-#include <vector>
-#include <map>
+ #include <asiolink/io_address.h>
-#include <boost/noncopyable.hpp>
-#include <boost/shared_ptr.hpp>
-#include <dhcp/option.h>
+ #include <dhcp/duid.h>
++#include <dhcp/option.h>
+ #include <dhcp/subnet.h>
++#include <exceptions/exceptions.h>
++
++#include <boost/noncopyable.hpp>
++#include <boost/shared_ptr.hpp>
++
+#include <fstream>
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
- #include <asiolink/io_address.h>
- #include <boost/shared_ptr.hpp>
- #include <dhcp/duid.h>
- #include <dhcp/option.h>
- #include <exceptions/exceptions.h>
-
/// @file dhcp/lease_mgr.h
/// @brief An abstract API for lease database
///
namespace isc {
namespace dhcp {
- /// @brief specifies unique subnet identifier
- /// @todo: Move this to subnet.h once ticket #2237 is merged
- typedef uint32_t SubnetID;
-
+/// @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 Structure that holds a lease for IPv4 address
///
/// For performance reasons it is a simple structure, not a class. If we chose
/// 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<uint8_t> 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<std::string, std::string> 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.
///
/// 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<uint32_t, uint32_t> 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,
--- /dev/null
+// 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 <algorithm>
+#include <iostream>
+#include <iterator>
+#include <map>
+#include <sstream>
+#include <string>
+#include <utility>
+
+#include <boost/foreach.hpp>
+#include <boost/scoped_ptr.hpp>
+#include <boost/algorithm/string.hpp>
+#include <exceptions/exceptions.h>
+#include <dhcp/lease_mgr_factory.h>
+
++#include <dhcp/memfile_lease_mgr.h>
+#ifdef HAVE_MYSQL
+#include <dhcp/mysql_lease_mgr.h>
+#endif
+
+using namespace std;
+
+namespace isc {
+namespace dhcp {
+
+boost::scoped_ptr<LeaseMgr>&
+LeaseMgrFactory::getLeaseMgrPtr() {
+ static boost::scoped_ptr<LeaseMgr> leaseMgrPtr;
+ return (leaseMgrPtr);
+}
+
+LeaseMgr::ParameterMap
+LeaseMgrFactory::parse(const std::string& dbconfig) {
+ LeaseMgr::ParameterMap mapped_tokens;
+
+ if (! dbconfig.empty()) {
+ vector<string> 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
--- /dev/null
-#include "memfile_lease_mgr.h"
+ // 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 <iostream>
-using namespace isc::dhcp::test;
++
++#include <dhcp/memfile_lease_mgr.h>
+
+ using namespace isc::dhcp;
-Memfile_LeaseMgr::Memfile_LeaseMgr(const std::string& dbconfig)
- : LeaseMgr(dbconfig) {
+
-Lease4Ptr Memfile_LeaseMgr::getLease4(isc::asiolink::IOAddress) const {
++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 ,
++Lease4Ptr Memfile_LeaseMgr::getLease4(const isc::asiolink::IOAddress&) const {
+ return (Lease4Ptr());
+ }
+
+ Lease4Collection Memfile_LeaseMgr::getLease4(const HWAddr& ) const {
+ return (Lease4Collection());
+ }
+
-bool Memfile_LeaseMgr::deleteLease4(uint32_t ) {
++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(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() {
++}
--- /dev/null
-#include <dhcp/lease_mgr.h>
+ // 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
+
-namespace test {
+ #include <boost/multi_index_container.hpp>
+ #include <boost/multi_index/indexed_by.hpp>
+ #include <boost/multi_index/ordered_index.hpp>
+ #include <boost/multi_index/member.hpp>
+
++#include <dhcp/lease_mgr.h>
++
+ namespace isc {
+ namespace dhcp {
- /// @param dbconfig database configuration
- Memfile_LeaseMgr(const std::string& dbconfig);
+
+ // 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.
+ ///
- virtual Lease4Ptr getLease4(isc::asiolink::IOAddress addr) const;
++ /// @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,
++ 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)
- /// @todo Not implemented yet
- ///
++ 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.
+ ///
- bool deleteLease4(uint32_t addr);
+ /// @param addr IPv4 address of the lease to be deleted.
+ ///
+ /// @return true if deletion was successful, false if no such lease exists
- std::string getVersion() const { return ("test-version"); }
++ 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.
-}; // end of isc::dhcp::test namespace
++ virtual std::pair<uint32_t, uint32_t> 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<Lease6, isc::asiolink::IOAddress, &Lease6::addr_>
+ >
+ >
+ > Lease6Storage; // Let the whole contraption be called Lease6Storage.
+
+ Lease6Storage storage6_;
+ };
+
-#endif // MEMFILE_LEASE_MGR_H
+ }; // end of isc::dhcp namespace
+ }; // end of isc namespace
+
++#endif // MEMFILE_LEASE_MGR_HSE4
++
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)
--- /dev/null
-using namespace isc::dhcp::test; // Memfile_LeaseMgr
+ // 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 <asiolink/io_address.h>
+ #include <dhcp/lease_mgr.h>
++#include <dhcp/lease_mgr_factory.h>
+ #include <dhcp/duid.h>
+ #include <dhcp/alloc_engine.h>
+ #include <dhcp/cfgmgr.h>
+ #include <dhcp/memfile_lease_mgr.h>
+ #include <boost/shared_ptr.hpp>
+ #include <boost/scoped_ptr.hpp>
+ #include <iostream>
+ #include <sstream>
+ #include <map>
+ #include <gtest/gtest.h>
+
+ using namespace std;
+ using namespace isc;
+ using namespace isc::asiolink;
+ using namespace isc::dhcp;
- leasemgr_ = new 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<DUID>(new DUID(vector<uint8_t>(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::instance().destroy_instance();
- leasemgr_ = NULL;
++ 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* leasemgr_;
++ factory_.destroy();
+ }
+
+ DuidPtr duid_;
+ uint32_t iaid_;
+ Subnet6Ptr subnet_;
+ Pool6Ptr pool_;
- EXPECT_TRUE(first->hwaddr_ == second->hwaddr_);
++ 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_);
- Lease6Ptr from_mgr = LeaseMgr::instance().getLease6(lease->addr_);
+ 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<AllocEngine> 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<AllocEngine> 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<AllocEngine> 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
- ASSERT_TRUE(LeaseMgr::instance().addLease(used));
++ 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<AllocEngine> 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<DUID>(new DUID(vector<uint8_t>(8, 0xff)));
+ Lease6Ptr used(new Lease6(Lease6::LEASE_IA_NA, IOAddress("2001:db8:1::1f"),
+ duid2, 1, 2, 3, 4, 5, subnet_->getID()));
- Lease6Ptr from_mgr = LeaseMgr::instance().getLease6(lease->addr_);
++ 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<AllocEngine> 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 = 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<IOAddress, int> 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
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<uint32_t, uint32_t> 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<isc::dhcp::Lease4>&) {
- return (false);
- }
-
- bool Memfile_LeaseMgr::addLease(const boost::shared_ptr<isc::dhcp::Lease6>&) {
- 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);
}
- // 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.
+// There's no point in calling any other methods in LeaseMgr, as they
-TEST(Lease6, ctor) {
++// 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, 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
--- /dev/null
--- /dev/null
++// 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 <iostream>
++#include <sstream>
++#include <gtest/gtest.h>
++#include <asiolink/io_address.h>
++
++#include <dhcp/lease_mgr.h>
++#include <dhcp/duid.h>
++#include <dhcp/memfile_lease_mgr.h>
++
++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<Memfile_LeaseMgr> 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<Memfile_LeaseMgr> 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