BIND 10 master, updated. 51adf02fc69bf854a8b73b97cd481a009c5b428b [master] ChangeLog for trac2414

BIND 10 source code commits bind10-changes at lists.isc.org
Fri Nov 9 17:02:17 UTC 2012


The branch, master has been updated
       via  51adf02fc69bf854a8b73b97cd481a009c5b428b (commit)
       via  b3526430f02aa3dc3273612524d23137b8f1fe87 (commit)
       via  f9e9ca679889cd4b177c72c588931d4e79300989 (commit)
       via  4067c8bb2f66712c4d442fe07d39dbab84afbf27 (commit)
       via  e97b6bb7995463200de064519d705023f578da96 (commit)
       via  fed0360031161014bfdbc11d4e38adeaed697150 (commit)
       via  89886bb661e47985e05643ced62d0da4c14fdd8d (commit)
       via  beca6b4aefcb8aeb6f54added3bcd025c2196613 (commit)
       via  9d9c83375c4fd9d67436030bbfc6f285e271d399 (commit)
       via  c778e931a645b277b7e18a83f702bb08a0173ffb (commit)
       via  aa1643faad572dfc6c91d0fac707f4cac4d5b7a6 (commit)
       via  f25544eeedb66a4f5bb0e0c41387e2e58555daa9 (commit)
       via  50c262d64042f0350cc887b1f51000294bc74b5c (commit)
       via  75622a763d3a717f00efa2cc831108a60f5d2ed8 (commit)
       via  cad4fb56e85555a48088a9f1e915f32057c2f8ee (commit)
       via  9833efc0691e4b80f29a135bfd56fc97e6b0c847 (commit)
       via  4838e909a1458198896cd2911488b2d3d442952f (commit)
       via  fd94c6a10376330c5e87d9b618c422b9f9832708 (commit)
       via  961921abc4ca4874d46f39f8db391601b184e6af (commit)
       via  ae39613c152173ff88cfbaa32f379613c81b5a7d (commit)
       via  3d69190c34d28e64ddbd31439968402b8c903475 (commit)
       via  3d7a6b9c7e01032d583a61f5aa10065cb31fd5e1 (commit)
       via  fd7e25adfafa0696f3c13ae5f504ceb7a110c23e (commit)
       via  8a8750b7ee739de6208df6b25c9c9c7e11fb0434 (commit)
       via  f66ffd87e1d460956a8c0bd22700f03b7c3fd0f8 (commit)
       via  d9c1638e3498459671b8257a8c3529569a3a8fd9 (commit)
       via  b8b87c941fa3b5973dddd0e88220dd435bec699c (commit)
       via  f63a6cabd8ecfca7465f1856a3836bdd72ea34f3 (commit)
       via  90bb56b150b1c51690dffce834bff4b91f754111 (commit)
       via  ae8d436d86973d85dbf512ca39167b41dcdb6a7c (commit)
       via  ecaa3bfbcea80a640778d8f0629e7976d87bde92 (commit)
       via  bd3faa6a3d1d5628509b725ecb61ef91ee3c2bb0 (commit)
       via  4d2adcc95c4a49ff7efa67a1f9e188a9e3e96dca (commit)
       via  53a57b7f2a75f4dded32297f3f95d5700f536036 (commit)
       via  23d08556872bff7288ccd690a09010769639bff1 (commit)
       via  fdcffb798707a238b43f24a7dcc725acc4a4c962 (commit)
       via  2e7420da8bcb358bb4c8bb6d3a8c42e3fc26d53a (commit)
      from  c8b32f1adba0c274dc765bb162e3b67a1fa43ca9 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 51adf02fc69bf854a8b73b97cd481a009c5b428b
Author: Stephen Morris <stephen at isc.org>
Date:   Fri Nov 9 16:58:04 2012 +0000

    [master] ChangeLog for trac2414

-----------------------------------------------------------------------

Summary of changes:
 ChangeLog                                     |    5 +
 src/bin/dhcp6/dhcp6_messages.mes              |   51 +-
 src/bin/dhcp6/dhcp6_srv.cc                    |  292 ++++++++---
 src/bin/dhcp6/dhcp6_srv.h                     |   62 ++-
 src/bin/dhcp6/tests/config_parser_unittest.cc |   36 +-
 src/bin/dhcp6/tests/dhcp6_srv_unittest.cc     |  694 +++++++++++++++++++++----
 src/lib/dhcp/Makefile.am                      |   17 +-
 src/lib/dhcp/addr_utilities.cc                |   12 +-
 src/lib/dhcp/alloc_engine.cc                  |    1 +
 src/lib/dhcp/cfgmgr.cc                        |    2 +-
 src/lib/dhcp/dhcp6.h                          |    9 +-
 src/lib/dhcp/duid.cc                          |   19 +-
 src/lib/dhcp/duid.h                           |   11 +-
 src/lib/dhcp/iface_mgr.cc                     |    9 +
 src/lib/dhcp/{tests => }/memfile_lease_mgr.cc |   17 +-
 src/lib/dhcp/{tests => }/memfile_lease_mgr.h  |    0
 src/lib/dhcp/option6_ia.cc                    |    2 +-
 src/lib/dhcp/subnet.cc                        |    7 +
 src/lib/dhcp/subnet.h                         |   12 +
 src/lib/dhcp/tests/Makefile.am                |    2 -
 src/lib/dhcp/tests/alloc_engine_unittest.cc   |   12 +-
 src/lib/dhcp/tests/cfgmgr_unittest.cc         |   24 +-
 src/lib/dhcp/tests/duid_unittest.cc           |    8 +
 src/lib/dhcp/tests/lease_mgr_unittest.cc      |   23 +-
 src/lib/dhcp/tests/subnet_unittest.cc         |   26 +
 tests/tools/perfdhcp/command_options.cc       |    5 +-
 26 files changed, 1099 insertions(+), 259 deletions(-)
 rename src/lib/dhcp/{tests => }/memfile_lease_mgr.cc (81%)
 rename src/lib/dhcp/{tests => }/memfile_lease_mgr.h (100%)

-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index 07474ee..d69f499 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+501.	[func]      tomek
+	Added DHCPv6 allocation engine, now used in the processing of DHCPv6
+	messages.
+	(Trac #2414, git b3526430f02aa3dc3273612524d23137b8f1fe87)
+
 500.	[bug]		jinmei
 	Corrected the autoconf example in the examples directory so it can
 	use the configured path to Boost to check availability of the BIND 10
diff --git a/src/bin/dhcp6/dhcp6_messages.mes b/src/bin/dhcp6/dhcp6_messages.mes
index aee34b5..5f9cd02 100644
--- a/src/bin/dhcp6/dhcp6_messages.mes
+++ b/src/bin/dhcp6/dhcp6_messages.mes
@@ -30,6 +30,11 @@ from the BIND 10 control system by the IPv6 DHCP server.
 A debug message indicating that the IPv6 DHCP server has received an
 updated configuration from the BIND 10 configuration system.
 
+% DHCP6_DB_BACKEND_STARTED Lease database started (backend type: %1)
+This informational message is printed every time DHCPv6 is started.
+It indicates what database backend type is being to store lease and
+other information.
+
 % DHCP6_NOT_RUNNING IPv6 DHCP server is not running
 A warning message is issued when an attempt is made to shut down the
 IPv6 DHCP server but it is not running.
@@ -42,6 +47,27 @@ interfaces and is therefore shutting down.
 A debug message issued during startup, this indicates that the IPv6 DHCP
 server is about to open sockets on the specified port.
 
+% DHCP6_LEASE_ADVERT Lease %1 advertised (client duid=%2, iaid=%3)
+This debug message indicates that the server successfully advertised
+a lease. It is up to the client to choose one server out of othe advertised
+and continue allocation with that server. This is a normal behavior and
+indicates successful operation.
+
+% DHCP6_LEASE_ALLOC lease %1 has been allocated (client duid=%2, iaid=%3)
+This debug message indicates that the server successfully granted (in
+response to client's REQUEST message) a lease. This is a normal behavior
+and incicates successful operation.
+
+% DHCP6_LEASE_ADVERT_FAIL failed to advertise a lease for client duid=%1, iaid=%2
+This message indicates that the server failed to advertise (in response to
+received SOLICIT) a lease for a given client. There may be many reasons for
+such failure. Each specific failure is logged in a separate log entry.
+
+% DHCP6_LEASE_ALLOC_FAIL failed to grant a lease for client duid=%1, iaid=%2
+This message indicates that the server failed to grant (in response to
+received REQUEST) a lease for a given client. There may be many reasons for
+such failure. Each specific failure is logged in a separate log entry.
+
 % DHCP6_PACKET_PARSE_FAIL failed to parse incoming packet
 The IPv6 DHCP server has received a packet that it is unable to interpret.
 
@@ -50,7 +76,7 @@ The IPv6 DHCP server tried to receive a packet but an error
 occured during this attempt. The reason for the error is included in
 the message.
 
-% DHCP6_PACKET_RECEIVED %1 (type %2) packet received
+% DHCP6_PACKET_RECEIVED %1 packet received
 A debug message noting that the server has received the specified type
 of packet.  Note that a packet marked as UNKNOWN may well be a valid
 DHCP packet, just a type not expected by the server (e.g. it will report
@@ -66,10 +92,15 @@ This error is output if the server failed to assemble the data to be
 returned to the client into a valid packet.  The reason is most likely
 to be to a programming error: please raise a bug report.
 
-% DHCP6_QUERY_DATA received packet length %1, data length %2, data is <%3>
+% DHCP6_PROCESS_IA_NA_REQUEST server is processing IA_NA option (duid=%1, iaid=%2, hint=%3)
+This is a debug message that indicates a processing of received IA_NA
+option. It may optionally contain an address that may be used by the server
+as a hint for possible requested address.
+
+% DHCP6_QUERY_DATA received packet length %1, data length %2, data is %3
 A debug message listing the data received from the client or relay.
 
-% DHCP6_RESPONSE_DATA responding with packet type %1 data is <%2>
+% DHCP6_RESPONSE_DATA responding with packet type %1 data is %2
 A debug message listing the data returned to the client.
 
 % DHCP6_SERVER_FAILED server failed: %1
@@ -110,6 +141,18 @@ This is a debug message issued during the IPv6 DHCP server startup.
 It lists some information about the parameters with which the server
 is running.
 
+% DHCP6_SUBNET_SELECTED the %1 subnet was selected for client assignment
+This is a debug message informing that a given subnet was selected. It will
+be used for address and option assignment. This is one of the early steps
+in the processing of incoming client message.
+
+% DHCP6_SUBNET_SELECTION_FAILED failed to select a subnet for incoming packet, src=%1 type=%2
+This warning message is output when a packet was received from a subnet for
+which the DHCPv6 server has not been configured. The cause is most likely due
+to a misconfiguration of the server. The packet processing will continue, but
+the response will only contain generic configuration parameters and no
+addresses or prefixes.
+
 % DHCP6_NO_SUBNET_DEF_OPT failed to find subnet for address %1 when adding default options
 This warning message indicates that when attempting to add default options to a response,
 the server found that it was not configured to support the subnet from which the DHCPv6
@@ -130,7 +173,7 @@ This is a debug message that is issued every time the server receives a
 configuration. That happens start up and also when a server configuration
 change is committed by the administrator.
 
-% DHCP6_CONFIG_NEW_SUBNET A new subnet has been added to configuration: %1
+% DHCP6_CONFIG_NEW_SUBNET a new subnet has been added to configuration: %1
 This is an informational message reporting that the configuration has
 been extended to include the specified subnet.
 
diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc
index 946bccb..7b1f721 100644
--- a/src/bin/dhcp6/dhcp6_srv.cc
+++ b/src/bin/dhcp6/dhcp6_srv.cc
@@ -31,6 +31,14 @@
 #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/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>
 
@@ -40,57 +48,59 @@ using namespace isc::dhcp;
 using namespace isc::util;
 using namespace std;
 
-const std::string HARDCODED_LEASE = "2001:db8:1::1234:abcd";
-const uint32_t HARDCODED_T1 = 1500; // in seconds
-const uint32_t HARDCODED_T2 = 2600; // in seconds
-const uint32_t HARDCODED_PREFERRED_LIFETIME = 3600; // in seconds
-const uint32_t HARDCODED_VALID_LIFETIME = 7200; // in seconds
-const std::string HARDCODED_DNS_SERVER = "2001:db8:1::1";
+namespace isc {
+namespace dhcp {
 
-Dhcpv6Srv::Dhcpv6Srv(uint16_t port) {
-    if (port == 0) {
-        // used for testing purposes. Some tests, e.g. configuration parser,
-        // require Dhcpv6Srv object, but they don't really need it to do
-        // anything. This speed up and simplifies the tests.
-        return;
-    }
+Dhcpv6Srv::Dhcpv6Srv(uint16_t port) : alloc_engine_(), serverid_(),
+                                      shutdown_(false) {
 
     LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_OPEN_SOCKET).arg(port);
 
     // Initialize objects required for DHCP server operation.
     try {
-
         // Initialize standard DHCPv6 option definitions. This function
         // may throw bad_alloc if system goes out of memory during the
         // creation if option definitions. It may also throw isc::Unexpected
         // if definitions are wrong. This would mean error in implementation.
         initStdOptionDefs();
 
-        // Call IfaceMgr::instance() will create instance of Interface
-        // Manager (it's a singleton). It may throw if things go wrong.
-        if (IfaceMgr::instance().countIfaces() == 0) {
-            LOG_ERROR(dhcp6_logger, DHCP6_NO_INTERFACES);
-            shutdown_ = true;
-            return;
+        // Port 0 is used for testing purposes. It means that the server should
+        // not open any sockets at all. Some tests, e.g. configuration parser,
+        // require Dhcpv6Srv object, but they don't really need it to do
+        // anything. This speed up and simplifies the tests.
+        if (port > 0) {
+            if (IfaceMgr::instance().countIfaces() == 0) {
+                LOG_ERROR(dhcp6_logger, DHCP6_NO_INTERFACES);
+                shutdown_ = true;
+                return;
+            }
+            IfaceMgr::instance().openSockets6(port);
         }
 
-        IfaceMgr::instance().openSockets6(port);
-
         setServerID();
 
-        /// @todo: instantiate LeaseMgr here once it is imlpemented.
-
     } catch (const std::exception &e) {
         LOG_ERROR(dhcp6_logger, DHCP6_SRV_CONSTRUCT_ERROR).arg(e.what());
         shutdown_ = true;
         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("");
+
+    LOG_INFO(dhcp6_logger, DHCP6_DB_BACKEND_STARTED)
+        .arg(LeaseMgr::instance().getName());
+
+    // Instantiate allocation engine
+    alloc_engine_.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100));
 }
 
 Dhcpv6Srv::~Dhcpv6Srv() {
     IfaceMgr::instance().closeSockets();
+
+    LeaseMgr::destroy_instance();
 }
 
 void Dhcpv6Srv::shutdown() {
@@ -100,7 +110,12 @@ void Dhcpv6Srv::shutdown() {
 
 bool Dhcpv6Srv::run() {
     while (!shutdown_) {
-        /// @todo: calculate actual timeout once we have lease database
+        /// @todo: calculate actual timeout to the next event (e.g. lease
+        /// expiration) once we have lease database. The idea here is that
+        /// it is possible to do everything in a single process/thread.
+        /// For now, we are just calling select for 1000 seconds. There
+        /// were some issues reported on some systems when calling select()
+        /// with too large values. Unfortunately, I don't recall the details.
         int timeout = 1000;
 
         // client's message and server's response
@@ -120,10 +135,9 @@ bool Dhcpv6Srv::run() {
                 continue;
             }
             LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_PACKET_RECEIVED)
-                      .arg(serverReceivedPacketName(query->getType()))
-                      .arg(query->getType());
+                      .arg(serverReceivedPacketName(query->getType()));
             LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL_DATA, DHCP6_QUERY_DATA)
-                      .arg(query->getType())
+                      .arg(static_cast<int>(query->getType()))
                       .arg(query->getBuffer().getLength())
                       .arg(query->toText());
 
@@ -209,7 +223,7 @@ void Dhcpv6Srv::setServerID() {
 
     const IfaceMgr::IfaceCollection& ifaces = IfaceMgr::instance().getIfaces();
 
-    // let's find suitable interface
+    // Let's find suitable interface.
     for (IfaceMgr::IfaceCollection::const_iterator iface = ifaces.begin();
          iface != ifaces.end(); ++iface) {
         // All the following checks could be merged into one multi-condition
@@ -230,17 +244,17 @@ void Dhcpv6Srv::setServerID() {
             continue;
         }
 
-        // let's don't use loopback
+        // Let's don't use loopback.
         if (iface->flag_loopback_) {
             continue;
         }
 
-        // let's skip downed interfaces. It is better to use working ones.
+        // Let's skip downed interfaces. It is better to use working ones.
         if (!iface->flag_up_) {
             continue;
         }
 
-        // some interfaces (like lo on Linux) report 6-bytes long
+        // Some interfaces (like lo on Linux) report 6-bytes long
         // MAC adress 00:00:00:00:00:00. Let's not use such weird interfaces
         // to generate DUID.
         if (isRangeZero(iface->getMac(), iface->getMac() + iface->getMacLen())) {
@@ -256,37 +270,37 @@ void Dhcpv6Srv::setServerID() {
         seconds -= DUID_TIME_EPOCH;
 
         OptionBuffer srvid(8 + iface->getMacLen());
-        writeUint16(DUID_LLT, &srvid[0]);
+        writeUint16(DUID::DUID_LLT, &srvid[0]);
         writeUint16(HWTYPE_ETHERNET, &srvid[2]);
         writeUint32(static_cast<uint32_t>(seconds), &srvid[4]);
-        memcpy(&srvid[0]+8, iface->getMac(), iface->getMacLen());
+        memcpy(&srvid[0] + 8, iface->getMac(), iface->getMacLen());
 
         serverid_ = OptionPtr(new Option(Option::V6, D6O_SERVERID,
                                          srvid.begin(), srvid.end()));
         return;
     }
 
-    // if we reached here, there are no suitable interfaces found.
+    // If we reached here, there are no suitable interfaces found.
     // Either interface detection is not supported on this platform or
     // this is really weird box. Let's use DUID-EN instead.
     // See Section 9.3 of RFC3315 for details.
 
     OptionBuffer srvid(12);
-    writeUint16(DUID_EN, &srvid[0]);
+    writeUint16(DUID::DUID_EN, &srvid[0]);
     writeUint32(ENTERPRISE_ID_ISC, &srvid[2]);
 
     // Length of the identifier is company specific. I hereby declare
     // ISC "standard" of 6 bytes long pseudo-random numbers.
     srandom(time(NULL));
-    fillRandom(&srvid[6],&srvid[12]);
+    fillRandom(&srvid[6], &srvid[12]);
 
     serverid_ = OptionPtr(new Option(Option::V6, D6O_SERVERID,
                                      srvid.begin(), srvid.end()));
 }
 
 void Dhcpv6Srv::copyDefaultOptions(const Pkt6Ptr& question, Pkt6Ptr& answer) {
-    // add client-id
-    boost::shared_ptr<Option> clientid = question->getOption(D6O_CLIENTID);
+    // Add client-id.
+    OptionPtr clientid = question->getOption(D6O_CLIENTID);
     if (clientid) {
         answer->addOption(clientid);
     }
@@ -307,18 +321,7 @@ void Dhcpv6Srv::appendDefaultOptions(const Pkt6Ptr& question, Pkt6Ptr& answer) {
             .arg(question->getRemoteAddr().toText());
         return;
     }
-    // Add DNS_SERVERS option. It should have been configured.
-    const Subnet::OptionContainer& options = subnet->getOptions();
-    const Subnet::OptionContainerTypeIndex& idx = options.get<1>();
-    const Subnet::OptionContainerTypeRange range =
-        idx.equal_range(D6O_NAME_SERVERS);
-    // In theory we may have multiple options with the same
-    // option code. They are not differentiated right now
-    // until support for option spaces is implemented.
-    // Until that's the case, simply add the first found option.
-    if (std::distance(range.first, range.second) > 0) {
-        answer->addOption(range.first->option);
-    }
+
 }
 
 void Dhcpv6Srv::appendRequestedOptions(const Pkt6Ptr& question, Pkt6Ptr& answer) {
@@ -353,33 +356,173 @@ void Dhcpv6Srv::appendRequestedOptions(const Pkt6Ptr& question, Pkt6Ptr& answer)
     }
 }
 
+OptionPtr Dhcpv6Srv::createStatusCode(uint16_t code, const std::string& text) {
+
+    // @todo: Implement Option6_StatusCode and rewrite this code here
+    vector<uint8_t> data(text.c_str(), text.c_str() + text.length());
+    data.insert(data.begin(), static_cast<uint8_t>(code % 256));
+    data.insert(data.begin(), static_cast<uint8_t>(code >> 8));
+    OptionPtr status(new Option(Option::V6, D6O_STATUS_CODE, data));
+    return (status);
+}
+
+Subnet6Ptr Dhcpv6Srv::selectSubnet(const Pkt6Ptr& question) {
+    Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(question->getRemoteAddr());
+
+    return (subnet);
+}
+
 void Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer) {
-    /// TODO Rewrite this once LeaseManager is implemented.
-
-    // answer client's IA (this is mostly a dummy,
-    // so let's answer only first IA and hope there is only one)
-    boost::shared_ptr<Option> ia_opt = question->getOption(D6O_IA_NA);
-    if (ia_opt) {
-        // found IA
-        Option* tmp = ia_opt.get();
-        Option6IA* ia_req = dynamic_cast<Option6IA*>(tmp);
-        if (ia_req) {
-            boost::shared_ptr<Option6IA>
-                ia_rsp(new Option6IA(D6O_IA_NA, ia_req->getIAID()));
-            ia_rsp->setT1(HARDCODED_T1);
-            ia_rsp->setT2(HARDCODED_T2);
-            boost::shared_ptr<Option6IAAddr>
-                addr(new Option6IAAddr(D6O_IAADDR,
-                                       IOAddress(HARDCODED_LEASE),
-                                       HARDCODED_PREFERRED_LIFETIME,
-                                       HARDCODED_VALID_LIFETIME));
-            ia_rsp->addOption(addr);
-            answer->addOption(ia_rsp);
+
+    // We need to allocate addresses for all IA_NA options in the client's
+    // question (i.e. SOLICIT or REQUEST) message.
+
+    // We need to select a subnet the client is connected in.
+    Subnet6Ptr subnet = selectSubnet(question);
+    if (subnet) {
+        // This particular client is out of luck today. We do not have
+        // information about the subnet he is connected to. This likely means
+        // misconfiguration of the server (or some relays). We will continue to
+        // process this message, but our response will be almost useless: no
+        // addresses or prefixes, no subnet specific configuration etc. The only
+        // thing this client can get is some global information (like DNS
+        // servers).
+        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL_DATA, DHCP6_SUBNET_SELECTED)
+            .arg(subnet->toText());
+    } else {
+        // perhaps this should be logged on some higher level? This is most likely
+        // configuration bug.
+        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_BASIC, DHCP6_SUBNET_SELECTION_FAILED);
+    }
+
+    // @todo: We should implement Option6Duid some day, but we can do without it
+    // just fine for now
+
+    // Let's find client's DUID. Client is supposed to include its client-id
+    // option almost all the time (the only exception is an anonymous inf-request,
+    // but that is mostly a theoretical case). Our allocation engine needs DUID
+    // and will refuse to allocate anything to anonymous clients.
+    DuidPtr duid;
+    OptionPtr opt_duid = question->getOption(D6O_CLIENTID);
+    if (opt_duid) {
+        duid = DuidPtr(new DUID(opt_duid->getData()));
+    }
+
+    // Now that we have all information about the client, let's iterate over all
+    // received options and handle IA_NA options one by one and store our
+    // responses in answer message (ADVERTISE or REPLY).
+    //
+    // @todo: expand this to cover IA_PD and IA_TA once we implement support for
+    // prefix delegation and temporary addresses.
+    for (Option::OptionCollection::iterator opt = question->options_.begin();
+         opt != question->options_.end(); ++opt) {
+        switch (opt->second->getType()) {
+        case D6O_IA_NA: {
+            OptionPtr answer_opt = handleIA_NA(subnet, duid, question,
+                                   boost::dynamic_pointer_cast<Option6IA>(opt->second));
+            if (answer_opt) {
+                answer->addOption(answer_opt);
+            }
+            break;
+        }
+        default:
+            break;
         }
     }
 }
 
+OptionPtr Dhcpv6Srv::handleIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid, Pkt6Ptr question,
+                                 boost::shared_ptr<Option6IA> ia) {
+    // If there is no subnet selected for handling this IA_NA, the only thing to do left is
+    // to say that we are sorry, but the user won't get an address. As a convenience, we
+    // use a different status text to indicate that (compare to the same status code,
+    // but different wording below)
+    if (!subnet) {
+        // Create empty IA_NA option with IAID matching the request.
+        boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
+
+        // Insert status code NoAddrsAvail.
+        ia_rsp->addOption(createStatusCode(STATUS_NoAddrsAvail, "Sorry, no subnet available."));
+        return (ia_rsp);
+    }
+
+    // Check if the client sent us a hint in his IA_NA. Clients may send an
+    // address in their IA_NA options as a suggestion (e.g. the last address
+    // they used before).
+    boost::shared_ptr<Option6IAAddr> hintOpt = boost::dynamic_pointer_cast<Option6IAAddr>
+                                        (ia->getOption(D6O_IAADDR));
+    IOAddress hint("::");
+    if (hintOpt) {
+        hint = hintOpt->getAddress();
+    }
+
+    LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_PROCESS_IA_NA_REQUEST)
+        .arg(duid?duid->toText():"(no-duid)").arg(ia->getIAID())
+        .arg(hintOpt?hint.toText():"(no hint)");
+
+    // "Fake" allocation is processing of SOLICIT message. We pretend to do an
+    // allocation, but we do not put the lease in the database. That is ok,
+    // because we do not guarantee that the user will get that exact lease. If
+    // the user selects this server to do actual allocation (i.e. sends REQUEST)
+    // it should include this hint. That will help us during the actual lease
+    // allocation.
+    bool fake_allocation = false;
+    if (question->getType() == DHCPV6_SOLICIT) {
+        /// @todo: Check if we support rapid commit
+        fake_allocation = true;
+    }
+
+    // Use allocation engine to pick a lease for this client. Allocation engine
+    // will try to honour the hint, but it is just a hint - some other address
+    // may be used instead. If fake_allocation is set to false, the lease will
+    // be inserted into the LeaseMgr as well.
+    Lease6Ptr lease = alloc_engine_->allocateAddress6(subnet, duid, ia->getIAID(),
+                                                      hint, fake_allocation);
+
+    // Create IA_NA that we will put in the response.
+    boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
+
+    if (lease) {
+        // We have a lease! Let's wrap its content into IA_NA option
+        // with IAADDR suboption.
+        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, fake_allocation?
+                  DHCP6_LEASE_ADVERT:DHCP6_LEASE_ALLOC)
+            .arg(lease->addr_.toText())
+            .arg(duid?duid->toText():"(no-duid)")
+            .arg(ia->getIAID());
+
+        ia_rsp->setT1(subnet->getT1());
+        ia_rsp->setT2(subnet->getT2());
+
+        boost::shared_ptr<Option6IAAddr>
+            addr(new Option6IAAddr(D6O_IAADDR,
+                                   lease->addr_,
+                                   lease->preferred_lft_,
+                                   lease->valid_lft_));
+        ia_rsp->addOption(addr);
+
+        // It would be possible to insert status code=0(success) as well,
+        // but this is considered waste of bandwidth as absence of status
+        // code is considered a success.
+    } else {
+        // Allocation engine did not allocate a lease. The engine logged
+        // cause of that failure. The only thing left is to insert
+        // status code to pass the sad news to the client.
+
+        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, fake_allocation?
+                  DHCP6_LEASE_ADVERT_FAIL:DHCP6_LEASE_ALLOC_FAIL)
+            .arg(duid?duid->toText():"(no-duid)")
+            .arg(ia->getIAID())
+            .arg(subnet->toText());
+
+        ia_rsp->addOption(createStatusCode(STATUS_NoAddrsAvail,
+                          "Sorry, no address could be allocated."));
+    }
+    return (ia_rsp);
+}
+
 Pkt6Ptr Dhcpv6Srv::processSolicit(const Pkt6Ptr& solicit) {
+
     Pkt6Ptr advertise(new Pkt6(DHCPV6_ADVERTISE, solicit->getTransid()));
 
     copyDefaultOptions(solicit, advertise);
@@ -486,3 +629,6 @@ void
 Dhcpv6Srv::initStdOptionDefs() {
     LibDHCP::initStdOptionDefs(Option::V6);
 }
+
+};
+};
diff --git a/src/bin/dhcp6/dhcp6_srv.h b/src/bin/dhcp6/dhcp6_srv.h
index 254ee7c..6574226 100644
--- a/src/bin/dhcp6/dhcp6_srv.h
+++ b/src/bin/dhcp6/dhcp6_srv.h
@@ -15,24 +15,33 @@
 #ifndef DHCPV6_SRV_H
 #define DHCPV6_SRV_H
 
+#include <iostream>
+
 #include <boost/noncopyable.hpp>
+#include <dhcp/alloc_engine.h>
 #include <dhcp/dhcp6.h>
-#include <dhcp/pkt6.h>
+#include <dhcp/duid.h>
 #include <dhcp/option.h>
+#include <dhcp/option6_ia.h>
 #include <dhcp/option_definition.h>
-#include <iostream>
+#include <dhcp/pkt6.h>
+#include <dhcp/subnet.h>
 
 namespace isc {
 
 namespace dhcp {
 /// @brief DHCPv6 server service.
 ///
-/// This singleton class represents DHCPv6 server. It contains all
+/// This class represents DHCPv6 server. It contains all
 /// top-level methods and routines necessary for server operation.
 /// In particular, it instantiates IfaceMgr, loads or generates DUID
 /// that is going to be used as server-identifier, receives incoming
 /// packets, processes them, manages leases assignment and generates
 /// appropriate responses.
+///
+/// @note Only one instance of this class is instantated as it encompasses
+///       the whole operation of the server.  Nothing, however, enforces the
+///       singleton status of the object.
 class Dhcpv6Srv : public boost::noncopyable {
 
 public:
@@ -53,7 +62,7 @@ public:
     /// @brief Destructor. Used during DHCPv6 service shutdown.
     virtual ~Dhcpv6Srv();
 
-    /// @brief Returns server-intentifier option
+    /// @brief Returns server-intentifier option.
     ///
     /// @return server-id option
     OptionPtr getServerID() { return serverid_; }
@@ -71,7 +80,7 @@ public:
     /// @brief Instructs the server to shut down.
     void shutdown();
 
-    /// @brief Return textual type of packet received by server
+    /// @brief Return textual type of packet received by server.
     ///
     /// Returns the name of valid packet received by the server (e.g. SOLICIT).
     /// If the packet is unknown - or if it is a valid DHCP packet but not one
@@ -148,7 +157,38 @@ protected:
     /// @param infRequest message received from client
     Pkt6Ptr processInfRequest(const Pkt6Ptr& infRequest);
 
-    /// @brief Copies required options from client message to server answer
+    /// @brief Creates status-code option.
+    ///
+    /// @param code status code value (see RFC3315)
+    /// @param text textual explanation (will be sent in status code option)
+    /// @return status-code option
+    OptionPtr createStatusCode(uint16_t code, const std::string& text);
+
+    /// @brief Selects a subnet for a given client's packet.
+    ///
+    /// @param question client's message
+    /// @return selected subnet (or NULL if no suitable subnet was found)
+    isc::dhcp::Subnet6Ptr selectSubnet(const Pkt6Ptr& question);
+
+    /// @brief Processes IA_NA option (and assigns addresses if necessary).
+    ///
+    /// Generates response to IA_NA. This typically includes selecting (and
+    /// allocating a lease in case of REQUEST) a lease and creating
+    /// IAADDR option. In case of allocation failure, it may contain
+    /// status code option with non-zero status, denoting cause of the
+    /// allocation failure.
+    ///
+    /// @param subnet subnet the client is connected to
+    /// @param duid client's duid
+    /// @param question client's message (typically SOLICIT or REQUEST)
+    /// @param ia pointer to client's IA_NA option (client's request)
+    /// @return IA_NA option (server's response)
+    OptionPtr handleIA_NA(const isc::dhcp::Subnet6Ptr& subnet,
+                          const isc::dhcp::DuidPtr& duid,
+                          isc::dhcp::Pkt6Ptr question,
+                          boost::shared_ptr<Option6IA> ia);
+
+    /// @brief Copies required options from client message to server answer.
     ///
     /// Copies options that must appear in any server response (ADVERTISE, REPLY)
     /// to client's messages (SOLICIT, REQUEST, RENEW, REBIND, DECLINE, RELEASE).
@@ -210,10 +250,16 @@ protected:
     void initStdOptionDefs();
 
 private:
-    /// server DUID (to be sent in server-identifier option)
+    /// @brief Allocation Engine.
+    /// Pointer to the allocation engine that we are currently using
+    /// It must be a pointer, because we will support changing engines
+    /// during normal operation (e.g. to use different allocators)
+    boost::shared_ptr<AllocEngine> alloc_engine_;
+
+    /// Server DUID (to be sent in server-identifier option)
     boost::shared_ptr<isc::dhcp::Option> serverid_;
 
-    /// indicates if shutdown is in progress. Setting it to true will
+    /// Indicates if shutdown is in progress. Setting it to true will
     /// initiate server shutdown procedure.
     volatile bool shutdown_;
 };
diff --git a/src/bin/dhcp6/tests/config_parser_unittest.cc b/src/bin/dhcp6/tests/config_parser_unittest.cc
index 929a6d2..780b5db 100644
--- a/src/bin/dhcp6/tests/config_parser_unittest.cc
+++ b/src/bin/dhcp6/tests/config_parser_unittest.cc
@@ -41,12 +41,11 @@ namespace {
 
 class Dhcp6ParserTest : public ::testing::Test {
 public:
-    Dhcp6ParserTest()
-    :rcode_(-1) {
-        // Open port 0 means to not do anything at all. We don't want to
+    Dhcp6ParserTest() :rcode_(-1), srv_(0) {
+        // srv_(0) means to not open any sockets. We don't want to
         // deal with sockets here, just check if configuration handling
         // is sane.
-        srv_ = new Dhcpv6Srv(0);
+
         // Create instances of option definitions and put them into storage.
         // This is normally initialized by the server when calling run()
         // run() function.
@@ -54,7 +53,6 @@ public:
     }
 
     ~Dhcp6ParserTest() {
-        delete srv_;
     };
 
     /// @brief Create the simple configuration with single option.
@@ -133,7 +131,7 @@ public:
         ConstElementPtr x;
         std::string config = createConfigWithOption(param_value, parameter);
         ElementPtr json = Element::fromJSON(config);
-        EXPECT_NO_THROW(x = configureDhcp6Server(*srv_, json));
+        EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
         ASSERT_TRUE(x);
         comment_ = parseAnswer(rcode_, x);
         ASSERT_EQ(1, rcode_);
@@ -179,7 +177,7 @@ public:
         EXPECT_TRUE(memcmp(expected_data, data, expected_data_len));
     }
 
-    Dhcpv6Srv* srv_;
+    Dhcpv6Srv srv_;
 
     int rcode_;
     ConstElementPtr comment_;
@@ -192,7 +190,7 @@ TEST_F(Dhcp6ParserTest, version) {
 
     ConstElementPtr x;
 
-    EXPECT_NO_THROW(x = configureDhcp6Server(*srv_,
+    EXPECT_NO_THROW(x = configureDhcp6Server(srv_,
                     Element::fromJSON("{\"version\": 0}")));
 
     // returned value must be 0 (configuration accepted)
@@ -207,7 +205,7 @@ TEST_F(Dhcp6ParserTest, bogusCommand) {
 
     ConstElementPtr x;
 
-    EXPECT_NO_THROW(x = configureDhcp6Server(*srv_,
+    EXPECT_NO_THROW(x = configureDhcp6Server(srv_,
                     Element::fromJSON("{\"bogus\": 5}")));
 
     // returned value must be 1 (configuration parse error)
@@ -223,7 +221,7 @@ TEST_F(Dhcp6ParserTest, emptySubnet) {
 
     ConstElementPtr status;
 
-    EXPECT_NO_THROW(status = configureDhcp6Server(*srv_,
+    EXPECT_NO_THROW(status = configureDhcp6Server(srv_,
                     Element::fromJSON("{ \"interface\": [ \"all\" ],"
                                       "\"preferred-lifetime\": 3000,"
                                       "\"rebind-timer\": 2000, "
@@ -255,7 +253,7 @@ TEST_F(Dhcp6ParserTest, subnetGlobalDefaults) {
 
     ElementPtr json = Element::fromJSON(config);
 
-    EXPECT_NO_THROW(status = configureDhcp6Server(*srv_, json));
+    EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
 
     // check if returned status is OK
     ASSERT_TRUE(status);
@@ -294,7 +292,7 @@ TEST_F(Dhcp6ParserTest, subnetLocal) {
 
     ElementPtr json = Element::fromJSON(config);
 
-    EXPECT_NO_THROW(status = configureDhcp6Server(*srv_, json));
+    EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
 
     // returned value should be 0 (configuration success)
     ASSERT_TRUE(status);
@@ -327,7 +325,7 @@ TEST_F(Dhcp6ParserTest, poolOutOfSubnet) {
 
     ElementPtr json = Element::fromJSON(config);
 
-    EXPECT_NO_THROW(status = configureDhcp6Server(*srv_, json));
+    EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
 
     // returned value must be 2 (values error)
     // as the pool does not belong to that subnet
@@ -355,7 +353,7 @@ TEST_F(Dhcp6ParserTest, poolPrefixLen) {
 
     ElementPtr json = Element::fromJSON(config);
 
-    EXPECT_NO_THROW(x = configureDhcp6Server(*srv_, json));
+    EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
 
     // returned value must be 1 (configuration parse error)
     ASSERT_TRUE(x);
@@ -397,7 +395,7 @@ TEST_F(Dhcp6ParserTest, optionDataDefaults) {
 
     ElementPtr json = Element::fromJSON(config);
 
-    EXPECT_NO_THROW(x = configureDhcp6Server(*srv_, json));
+    EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
     ASSERT_TRUE(x);
     comment_ = parseAnswer(rcode_, x);
     ASSERT_EQ(0, rcode_);
@@ -472,7 +470,7 @@ TEST_F(Dhcp6ParserTest, optionDataInSingleSubnet) {
 
     ElementPtr json = Element::fromJSON(config);
 
-    EXPECT_NO_THROW(x = configureDhcp6Server(*srv_, json));
+    EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
     ASSERT_TRUE(x);
     comment_ = parseAnswer(rcode_, x);
     ASSERT_EQ(0, rcode_);
@@ -538,7 +536,7 @@ TEST_F(Dhcp6ParserTest, optionDataInMultipleSubnets) {
 
     ElementPtr json = Element::fromJSON(config);
 
-    EXPECT_NO_THROW(x = configureDhcp6Server(*srv_, json));
+    EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
     ASSERT_TRUE(x);
     comment_ = parseAnswer(rcode_, x);
     ASSERT_EQ(0, rcode_);
@@ -656,7 +654,7 @@ TEST_F(Dhcp6ParserTest, optionDataLowerCase) {
     std::string config = createConfigWithOption("0a0b0C0D", "data");
     ElementPtr json = Element::fromJSON(config);
 
-    EXPECT_NO_THROW(x = configureDhcp6Server(*srv_, json));
+    EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
     ASSERT_TRUE(x);
     comment_ = parseAnswer(rcode_, x);
     ASSERT_EQ(0, rcode_);
@@ -697,7 +695,7 @@ TEST_F(Dhcp6ParserTest, stdOptionData) {
     std::string config = createConfigWithOption(params);
     ElementPtr json = Element::fromJSON(config);
 
-    EXPECT_NO_THROW(x = configureDhcp6Server(*srv_, json));
+    EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
     ASSERT_TRUE(x);
     comment_ = parseAnswer(rcode_, x);
     ASSERT_EQ(0, rcode_);
diff --git a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
index ef53bb4..f1cf7de 100644
--- a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
+++ b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
@@ -13,83 +13,218 @@
 // PERFORMANCE OF THIS SOFTWARE.
 
 #include <config.h>
-#include <iostream>
+
 #include <fstream>
+#include <iostream>
 #include <sstream>
 
-#include <arpa/inet.h>
 #include <gtest/gtest.h>
 
-#include <dhcp6/config_parser.h>
-#include <dhcp6/dhcp6_srv.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/option6_ia.h>
+#include <dhcp/duid.h>
+#include <dhcp/lease_mgr.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 <config/ccsession.h>
+#include <dhcp6/config_parser.h>
+#include <dhcp6/dhcp6_srv.h>
 #include <util/buffer.h>
 #include <util/range_utilities.h>
-#include <boost/scoped_ptr.hpp>
 
-using namespace std;
 using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::config;
+using namespace isc::data;
 using namespace isc::dhcp;
 using namespace isc::util;
-using namespace isc::data;
-using namespace isc::config;
-using namespace isc::asiolink;
+using namespace std;
 
 // namespace has to be named, because friends are defined in Dhcpv6Srv class
 // Maybe it should be isc::test?
 namespace {
 
 class NakedDhcpv6Srv: public Dhcpv6Srv {
-    // "naked" Interface Manager, exposes internal fields
+    // "naked" Interface Manager, exposes internal members
 public:
-    NakedDhcpv6Srv():Dhcpv6Srv(DHCP6_SERVER_PORT + 10000) { }
+    NakedDhcpv6Srv(uint16_t port):Dhcpv6Srv(port) { }
 
-    boost::shared_ptr<Pkt6>
-    processSolicit(boost::shared_ptr<Pkt6>& request) {
-        return Dhcpv6Srv::processSolicit(request);
-    }
-    boost::shared_ptr<Pkt6>
-    processRequest(boost::shared_ptr<Pkt6>& request) {
-        return Dhcpv6Srv::processRequest(request);
-    }
+    using Dhcpv6Srv::processSolicit;
+    using Dhcpv6Srv::processRequest;
+    using Dhcpv6Srv::createStatusCode;
+    using Dhcpv6Srv::selectSubnet;
 };
 
 class Dhcpv6SrvTest : public ::testing::Test {
 public:
-    Dhcpv6SrvTest()
-        : rcode_(-1) {
+    // these are empty for now, but let's keep them around
+    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());
+        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
     // fe80::1234 link-local address on eth0 interface. Obviously
     // an attempt to bind this socket will fail.
-    Dhcpv6Srv* srv = NULL;
+    boost::scoped_ptr<Dhcpv6Srv> srv;
+
+    ASSERT_NO_THROW( {
+        // Skip opening any sockets
+        srv.reset(new Dhcpv6Srv(0));
+    });
+    srv.reset();
     ASSERT_NO_THROW({
         // open an unpriviledged port
-        srv = new Dhcpv6Srv(DHCP6_SERVER_PORT + 10000);
+        srv.reset(new Dhcpv6Srv(DHCP6_SERVER_PORT + 10000));
     });
-
-    delete srv;
 }
 
+// Test checks that DUID is generated properly
 TEST_F(Dhcpv6SrvTest, DUID) {
-    // tests that DUID is generated properly
 
     boost::scoped_ptr<Dhcpv6Srv> srv;
-    ASSERT_NO_THROW({
-        srv.reset(new Dhcpv6Srv(DHCP6_SERVER_PORT + 10000));
+    ASSERT_NO_THROW( {
+        srv.reset(new Dhcpv6Srv(0));
     });
 
     OptionPtr srvid = srv->getServerID();
@@ -111,7 +246,7 @@ TEST_F(Dhcpv6SrvTest, DUID) {
     uint16_t duid_type = data.readUint16();
     cout << "Duid-type=" << duid_type << endl;
     switch(duid_type) {
-    case DUID_LLT: {
+    case DUID::DUID_LLT: {
         // DUID must contain at least 6 bytes long MAC
         // + 8 bytes of fixed header
         EXPECT_GE(14, len);
@@ -135,7 +270,7 @@ TEST_F(Dhcpv6SrvTest, DUID) {
         EXPECT_TRUE(mac != zeros);
         break;
     }
-    case DUID_EN: {
+    case DUID::DUID_EN: {
         // there's not much we can check. Just simple
         // check if it is not all zeros
         vector<uint8_t> content(len-2);
@@ -143,7 +278,7 @@ TEST_F(Dhcpv6SrvTest, DUID) {
         EXPECT_FALSE(isRangeZero(content.begin(), content.end()));
         break;
     }
-    case DUID_LL: {
+    case DUID::DUID_LL: {
         // not supported yet
         cout << "Test not implemented for DUID-LL." << endl;
 
@@ -153,22 +288,24 @@ TEST_F(Dhcpv6SrvTest, DUID) {
         // and keep it despite hardware changes.
         break;
     }
-    case DUID_UUID: // not supported yet
+    case DUID::DUID_UUID: // not supported yet
     default:
         ADD_FAILURE() << "Not supported duid type=" << duid_type << endl;
         break;
     }
 }
 
-TEST_F(Dhcpv6SrvTest, solicitBasic) {
+// 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:1234::/80\" ],"
-        "    \"subnet\": \"2001:db8:1234::/64\", "
+        "    \"pool\": [ \"2001:db8:1::/64\" ],"
+        "    \"subnet\": \"2001:db8:1::/48\", "
         "    \"option-data\": [ {"
         "          \"name\": \"OPTION_DNS_SERVERS\","
         "          \"code\": 23,"
@@ -186,7 +323,7 @@ TEST_F(Dhcpv6SrvTest, solicitBasic) {
     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)));
 
     EXPECT_NO_THROW(x = configureDhcp6Server(*srv, json));
     ASSERT_TRUE(x);
@@ -194,98 +331,43 @@ TEST_F(Dhcpv6SrvTest, solicitBasic) {
 
     ASSERT_EQ(0, rcode_);
 
-    // a dummy content for client-id
-    OptionBuffer clntDuid(32);
-    for (int i = 0; i < 32; i++) {
-        clntDuid[i] = 100 + i;
-    }
-
     Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
-
-    boost::shared_ptr<Option6IA> ia =
-        boost::shared_ptr<Option6IA>(new Option6IA(D6O_IA_NA, 234));
-    ia->setT1(1501);
-    ia->setT2(2601);
-    sol->addOption(ia);
-
-    // 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); */
-
-    // 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
-
-    OptionPtr clientid = OptionPtr(new Option(Option::V6, D6O_CLIENTID,
-                                              clntDuid.begin(),
-                                              clntDuid.begin() + 16));
+    sol->setRemoteAddr(IOAddress("fe80::abcd"));
+    sol->addOption(generateIA(234, 1500, 3000));
+    OptionPtr clientid = generateClientId();
     sol->addOption(clientid);
 
-    boost::shared_ptr<Pkt6> reply = srv->processSolicit(sol);
+    // 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(reply);
-
-    EXPECT_EQ(DHCPV6_ADVERTISE, reply->getType());
-    EXPECT_EQ(1234, reply->getTransid());
+    ASSERT_TRUE(adv);
 
     // We have not requested option with code 1000 so it should not
     // be included in the response.
-    ASSERT_FALSE(reply->getOption(1000));
+    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 one code equal to 1000.
-    std::vector<uint16_t> codes(1, 1000);
+    // 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.
-    reply = srv->processSolicit(sol);
-    ASSERT_TRUE(reply);
-
-    EXPECT_EQ(DHCPV6_ADVERTISE, reply->getType());
-
-    OptionPtr tmp = reply->getOption(D6O_IA_NA);
-    ASSERT_TRUE(tmp);
-
-    boost::shared_ptr<Option6IA> reply_ia =
-        boost::dynamic_pointer_cast<Option6IA>(tmp);
-    ASSERT_TRUE(reply_ia);
-    EXPECT_EQ(234, reply_ia->getIAID());
 
-    // check that there's an address included
-    EXPECT_TRUE(reply_ia->getOption(D6O_IAADDR));
+    // Need to process SOLICIT again after requesting new option.
+    adv = srv->processSolicit(sol);
+    ASSERT_TRUE(adv);
 
-    // check that server included our own client-id
-    tmp = reply->getOption(D6O_CLIENTID);
+    OptionPtr tmp = adv->getOption(D6O_NAME_SERVERS);
     ASSERT_TRUE(tmp);
-    EXPECT_EQ(clientid->getType(), tmp->getType());
-    ASSERT_EQ(clientid->len(), tmp->len());
-
-    EXPECT_TRUE(clientid->getData() == tmp->getData());
-
-    // 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());
 
-    EXPECT_TRUE(tmp->getData() == srv->getServerID()->getData());
- 
-    tmp = reply->getOption(D6O_NAME_SERVERS);
-    ASSERT_TRUE(tmp);
-    
     boost::shared_ptr<Option6AddrLst> reply_nameservers =
         boost::dynamic_pointer_cast<Option6AddrLst>(tmp);
     ASSERT_TRUE(reply_nameservers);
@@ -297,7 +379,7 @@ TEST_F(Dhcpv6SrvTest, solicitBasic) {
 
     // There is a dummy option with code 1000 we requested from a server.
     // Expect that this option is in server's response.
-    tmp = reply->getOption(1000);
+    tmp = adv->getOption(1000);
     ASSERT_TRUE(tmp);
 
     // Check that the option contains valid data (from configuration).
@@ -312,6 +394,361 @@ TEST_F(Dhcpv6SrvTest, solicitBasic) {
     // 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);
+
+    // 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);
+
+    // check if we get response at all
+    checkResponse(reply, DHCPV6_ADVERTISE, 1234);
+
+    OptionPtr tmp = reply->getOption(D6O_IA_NA);
+    ASSERT_TRUE(tmp);
+
+    // 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 we've got the address we requested
+    checkIAAddr(addr, hint, 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 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);
+
+    // 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
+    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);
+
+    // 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);
+
+    // 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 we've got the address we requested
+    checkIAAddr(addr, hint, subnet_->getPreferred(), subnet_->getValid());
+
+    // check DUIDs
+    checkServerId(reply, srv->getServerID());
+    checkClientId(reply, clientid);
+
+    // 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());
+}
+
+// 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)) );
+
+    Pkt6Ptr req1 = Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234));
+    Pkt6Ptr req2 = Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 2345));
+    Pkt6Ptr req3 = Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 3456));
+
+    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) {
@@ -357,4 +794,41 @@ TEST_F(Dhcpv6SrvTest, serverReceivedPacketName) {
     }
 }
 
+// This test verifies if the status code option is generated properly.
+TEST_F(Dhcpv6SrvTest, StatusCode) {
+    boost::scoped_ptr<NakedDhcpv6Srv> srv;
+    ASSERT_NO_THROW( srv.reset(new NakedDhcpv6Srv(0)) );
+
+    // a dummy content for client-id
+    uint8_t expected[] = {0x0, 0x3, 0x41, 0x42, 0x43, 0x44, 0x45};
+    OptionBuffer exp(expected, expected + sizeof(expected));
+
+    OptionPtr status = srv->createStatusCode(3, "ABCDE");
+
+    EXPECT_TRUE(status->getData() == exp);
+}
+
+// This test verifies if the selectSubnet() method works as expected.
+TEST_F(Dhcpv6SrvTest, SelectSubnet) {
+    boost::scoped_ptr<NakedDhcpv6Srv> srv;
+    ASSERT_NO_THROW( srv.reset(new NakedDhcpv6Srv(0)) );
+
+    Pkt6Ptr pkt = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
+
+    // check that the packets originating from local addresses can be
+    pkt->setRemoteAddr(IOAddress("fe80::abcd"));
+    EXPECT_EQ(subnet_, srv->selectSubnet(pkt));
+
+    // packets originating from subnet A will select subnet A
+    pkt->setRemoteAddr(IOAddress("2001:db8:1::6789"));
+    EXPECT_EQ(subnet_, srv->selectSubnet(pkt));
+
+    // packets from a subnet that is not supported will not get
+    // a subnet
+    pkt->setRemoteAddr(IOAddress("3000::faf"));
+    EXPECT_FALSE(srv->selectSubnet(pkt));
+
+    /// @todo: expand this test once support for relays is implemented
+}
+
 }   // end of anonymous namespace
diff --git a/src/lib/dhcp/Makefile.am b/src/lib/dhcp/Makefile.am
index 43a1b04..0d54a07 100644
--- a/src/lib/dhcp/Makefile.am
+++ b/src/lib/dhcp/Makefile.am
@@ -34,27 +34,30 @@ libb10_dhcp___la_SOURCES += pkt6.cc pkt6.h
 libb10_dhcp___la_SOURCES += pkt4.cc pkt4.h
 libb10_dhcp___la_SOURCES += duid.cc duid.h
 
+libb10_dhcp___la_CXXFLAGS = $(AM_CXXFLAGS)
+libb10_dhcp___la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
+libb10_dhcp___la_LIBADD   = $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
+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 += 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
 
 EXTRA_DIST  = README
 
-libb10_dhcp___la_CXXFLAGS = $(AM_CXXFLAGS)
-libb10_dhcp___la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
-libb10_dhcp___la_LIBADD   = $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
-libb10_dhcp___la_LIBADD  += $(top_builddir)/src/lib/util/libb10-util.la
-libb10_dhcp___la_LDFLAGS  = -no-undefined -version-info 2:0:0
-
 if USE_CLANGPP
 # Disable unused parameter warning caused by some of the
 # Boost headers when compiling with clang.
diff --git a/src/lib/dhcp/addr_utilities.cc b/src/lib/dhcp/addr_utilities.cc
index db35ca6..6ac3f14 100644
--- a/src/lib/dhcp/addr_utilities.cc
+++ b/src/lib/dhcp/addr_utilities.cc
@@ -16,6 +16,7 @@
 #include <exceptions/exceptions.h>
 #include <dhcp/addr_utilities.h>
 
+using namespace isc;
 using namespace isc::asiolink;
 
 namespace {
@@ -45,14 +46,13 @@ const uint8_t bitMask6[]= { 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
 /// @param len prefix length
 isc::asiolink::IOAddress firstAddrInPrefix6(const isc::asiolink::IOAddress& prefix,
                                             uint8_t len) {
-    uint8_t packed[V6ADDRESS_LEN];
-
     if (len > 128) {
         isc_throw(isc::BadValue,
                   "Too large netmask. 0..128 is allowed in IPv6");
     }
 
     // First we copy the whole address as 16 bytes.
+    uint8_t packed[V6ADDRESS_LEN];
     memcpy(packed, prefix.getAddress().to_v6().to_bytes().data(), 16);
 
     // If the length is divisible by 8, it is simple. We just zero out the host
@@ -90,11 +90,11 @@ isc::asiolink::IOAddress firstAddrInPrefix6(const isc::asiolink::IOAddress& pref
 /// @param len netmask length (0-32)
 isc::asiolink::IOAddress firstAddrInPrefix4(const isc::asiolink::IOAddress& prefix,
                                             uint8_t len) {
-    uint32_t addr = prefix;
     if (len > 32) {
         isc_throw(isc::BadValue, "Too large netmask. 0..32 is allowed in IPv4");
     }
 
+    uint32_t addr = prefix;
     return (IOAddress(addr & (~bitMask4[len])));
 }
 
@@ -107,11 +107,11 @@ isc::asiolink::IOAddress firstAddrInPrefix4(const isc::asiolink::IOAddress& pref
 /// @param len netmask length (0-32)
 isc::asiolink::IOAddress lastAddrInPrefix4(const isc::asiolink::IOAddress& prefix,
                                            uint8_t len) {
-    uint32_t addr = prefix;
     if (len > 32) {
         isc_throw(isc::BadValue, "Too large netmask. 0..32 is allowed in IPv4");
     }
 
+    uint32_t addr = prefix;
     return (IOAddress(addr | bitMask4[len]));
 }
 
@@ -124,15 +124,13 @@ isc::asiolink::IOAddress lastAddrInPrefix4(const isc::asiolink::IOAddress& prefi
 /// @param len netmask length (0-128)
 isc::asiolink::IOAddress lastAddrInPrefix6(const isc::asiolink::IOAddress& prefix,
                                            uint8_t len) {
-
-    uint8_t packed[V6ADDRESS_LEN];
-
     if (len > 128) {
         isc_throw(isc::BadValue,
                   "Too large netmask. 0..128 is allowed in IPv6");
     }
 
     // First we copy the whole address as 16 bytes.
+    uint8_t packed[V6ADDRESS_LEN];
     memcpy(packed, prefix.getAddress().to_v6().to_bytes().data(), 16);
 
     // if the length is divisible by 8, it is simple. We just fill the host part
diff --git a/src/lib/dhcp/alloc_engine.cc b/src/lib/dhcp/alloc_engine.cc
index 8ebefdc..650fd5f 100644
--- a/src/lib/dhcp/alloc_engine.cc
+++ b/src/lib/dhcp/alloc_engine.cc
@@ -13,6 +13,7 @@
 // PERFORMANCE OF THIS SOFTWARE.
 
 #include <alloc_engine.h>
+#include <string.h>
 
 #include <cstring>
 
diff --git a/src/lib/dhcp/cfgmgr.cc b/src/lib/dhcp/cfgmgr.cc
index d06544c..0e7c8f4 100644
--- a/src/lib/dhcp/cfgmgr.cc
+++ b/src/lib/dhcp/cfgmgr.cc
@@ -41,7 +41,7 @@ CfgMgr::getSubnet6(const isc::asiolink::IOAddress& hint) {
     // configuration. Such requirement makes sense in IPv4, but not in IPv6.
     // The server does not need to have a global address (using just link-local
     // is ok for DHCPv6 server) from the pool it serves.
-    if (subnets6_.size() == 1) {
+    if ((subnets6_.size() == 1) && hint.getAddress().to_v6().is_link_local()) {
         return (subnets6_[0]);
     }
 
diff --git a/src/lib/dhcp/dhcp6.h b/src/lib/dhcp/dhcp6.h
index 15c306d..22e7c8d 100644
--- a/src/lib/dhcp/dhcp6.h
+++ b/src/lib/dhcp/dhcp6.h
@@ -105,10 +105,11 @@ extern const int dhcpv6_type_name_max;
 
 /* DUID type definitions (RFC3315 section 9).
  */
-#define DUID_LLT        1
-#define DUID_EN         2
-#define DUID_LL         3
-#define DUID_UUID       4
+// see isc::dhcp::DUID::DUIDType enum in dhcp/duid.h
+// #define DUID_LLT        1
+// #define DUID_EN         2
+// #define DUID_LL         3
+// #define DUID_UUID       4
 
 // Define hardware types
 // Taken from http://www.iana.org/assignments/arp-parameters/
diff --git a/src/lib/dhcp/duid.cc b/src/lib/dhcp/duid.cc
index db7ba25..01dcfe1 100644
--- a/src/lib/dhcp/duid.cc
+++ b/src/lib/dhcp/duid.cc
@@ -12,11 +12,13 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-#include <vector>
 #include <exceptions/exceptions.h>
 #include <stdint.h>
 #include <util/io_utilities.h>
 #include <dhcp/duid.h>
+#include <vector>
+#include <sstream>
+#include <iomanip>
 
 namespace isc {
 namespace dhcp {
@@ -53,6 +55,21 @@ DUID::DUIDType DUID::getType() const {
     }
 }
 
+std::string DUID::toText() const {
+    std::stringstream tmp;
+    tmp << std::hex;
+    bool delim = false;
+    for (std::vector<uint8_t>::const_iterator it = duid_.begin();
+         it != duid_.end(); ++it) {
+        if (delim) {
+            tmp << ":";
+        }
+        tmp << std::setw(2) << std::setfill('0') << static_cast<unsigned int>(*it);
+        delim = true;
+    }
+    return (tmp.str());
+}
+
 bool DUID::operator == (const DUID& other) const {
     return (this->duid_ == other.duid_);
 }
diff --git a/src/lib/dhcp/duid.h b/src/lib/dhcp/duid.h
index 5f8ad58..be575dd 100644
--- a/src/lib/dhcp/duid.h
+++ b/src/lib/dhcp/duid.h
@@ -61,11 +61,14 @@ class DUID {
     /// @brief returns DUID type
     DUIDType getType() const;
 
-    // compares two DUIDs
-    bool operator == (const DUID& other) const;
+    /// returns textual prepresentation (e.g. 00:01:02:03:ff)
+    std::string toText() const;
 
-    // compares two DUIDs
-    bool operator != (const DUID& other) const;
+    /// compares two DUIDs
+    bool operator==(const DUID& other) const;
+
+    /// compares two DUIDs
+    bool operator!=(const DUID& other) const;
 
  protected:
     /// the actual content of the DUID
diff --git a/src/lib/dhcp/iface_mgr.cc b/src/lib/dhcp/iface_mgr.cc
index ecaa652..1c2a11c 100644
--- a/src/lib/dhcp/iface_mgr.cc
+++ b/src/lib/dhcp/iface_mgr.cc
@@ -247,6 +247,15 @@ bool IfaceMgr::openSockets6(const uint16_t port) {
                 continue;
             }
 
+            // Bind link-local addresses only. Otherwise we bind several sockets
+            // on interfaces that have several global addresses. For examples
+            // with interface with 2 global addresses, we would bind 3 sockets
+            // (one for link-local and two for global). That would result in
+            // getting each message 3 times.
+            if (!addr->getAddress().to_v6().is_link_local()){
+                continue;
+            }
+
             sock = openSocket(iface->getName(), *addr, port);
             if (sock < 0) {
                 isc_throw(SocketConfigError, "failed to open unicast socket");
diff --git a/src/lib/dhcp/memfile_lease_mgr.cc b/src/lib/dhcp/memfile_lease_mgr.cc
new file mode 100644
index 0000000..63023c4
--- /dev/null
+++ b/src/lib/dhcp/memfile_lease_mgr.cc
@@ -0,0 +1,124 @@
+// 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>
+#include "memfile_lease_mgr.h"
+
+using namespace isc::dhcp;
+using namespace isc::dhcp::test;
+
+Memfile_LeaseMgr::Memfile_LeaseMgr(const std::string& dbconfig)
+    : LeaseMgr(dbconfig) {
+    std::cout << "Warning: Using memfile database backend. It is usable for" << std::endl;
+    std::cout << "Warning: limited testing only. File support not implemented yet." << std::endl;
+    std::cout << "Warning: Leases will be lost after restart." << std::endl;
+}
+
+Memfile_LeaseMgr::~Memfile_LeaseMgr() {
+}
+
+bool Memfile_LeaseMgr::addLease(const Lease4Ptr&) {
+    return (false);
+}
+
+bool Memfile_LeaseMgr::addLease(const Lease6Ptr& lease) {
+    if (getLease6(lease->addr_)) {
+        // there is a lease with specified address already
+        return (false);
+    }
+    storage6_.insert(lease);
+    return (true);
+}
+
+Lease4Ptr Memfile_LeaseMgr::getLease4(isc::asiolink::IOAddress) const {
+    return (Lease4Ptr());
+}
+
+Lease4Collection Memfile_LeaseMgr::getLease4(const HWAddr& ) const {
+    return (Lease4Collection());
+}
+
+Lease4Ptr Memfile_LeaseMgr::getLease4(isc::asiolink::IOAddress ,
+                                      SubnetID) const {
+    return (Lease4Ptr());
+}
+
+Lease4Ptr Memfile_LeaseMgr::getLease4(const HWAddr&,
+                                      SubnetID) const {
+    return (Lease4Ptr());
+}
+
+
+Lease4Ptr Memfile_LeaseMgr::getLease4(const ClientId&,
+                                      SubnetID) const {
+    return (Lease4Ptr());
+}
+
+Lease4Collection Memfile_LeaseMgr::getLease4(const ClientId& ) const {
+    return (Lease4Collection());
+}
+
+Lease6Ptr Memfile_LeaseMgr::getLease6(const isc::asiolink::IOAddress& addr) const {
+    Lease6Storage::iterator l = storage6_.find(addr);
+    if (l == storage6_.end()) {
+        return (Lease6Ptr());
+    } else {
+        return (*l);
+    }
+}
+
+Lease6Collection Memfile_LeaseMgr::getLease6(const DUID& , uint32_t ) const {
+    return (Lease6Collection());
+}
+
+Lease6Ptr Memfile_LeaseMgr::getLease6(const DUID& duid, uint32_t iaid,
+                                      SubnetID subnet_id) const {
+    /// @todo: Slow, naive implementation. Write it using additional indexes
+    for (Lease6Storage::iterator l = storage6_.begin(); l != storage6_.end(); ++l) {
+        if ( (*((*l)->duid_) == duid) &&
+             ( (*l)->iaid_ == iaid) &&
+             ( (*l)->subnet_id_ == subnet_id)) {
+            return (*l);
+        }
+    }
+    return (Lease6Ptr());
+}
+
+void Memfile_LeaseMgr::updateLease4(const Lease4Ptr& ) {
+}
+
+void Memfile_LeaseMgr::updateLease6(const Lease6Ptr& ) {
+
+}
+
+bool Memfile_LeaseMgr::deleteLease4(uint32_t ) {
+    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."));
+}
diff --git a/src/lib/dhcp/memfile_lease_mgr.h b/src/lib/dhcp/memfile_lease_mgr.h
new file mode 100644
index 0000000..c5a41e6
--- /dev/null
+++ b/src/lib/dhcp/memfile_lease_mgr.h
@@ -0,0 +1,228 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef MEMFILE_LEASE_MGR_H
+#define MEMFILE_LEASE_MGR_H
+
+#include <dhcp/lease_mgr.h>
+#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>
+
+namespace isc {
+namespace dhcp {
+namespace test {
+
+// This is a concrete implementation of a Lease database.
+//
+// It is for testing purposes only. It is NOT a production code.
+//
+// It does not do anything useful now, and is used for abstract LeaseMgr
+// class testing. It may later evolve into more useful backend if the
+// need arises. We can reuse code from memfile benchmark. See code in
+// tests/tools/dhcp-ubench/memfile_bench.{cc|h}
+class Memfile_LeaseMgr : public LeaseMgr {
+public:
+
+    /// @brief The sole lease manager constructor
+    ///
+    /// dbconfig is a generic way of passing parameters. Parameters
+    /// are passed in the "name=value" format, separated by spaces.
+    /// Values may be enclosed in double quotes, if needed.
+    ///
+    /// @param dbconfig database configuration
+    Memfile_LeaseMgr(const std::string& dbconfig);
+
+    /// @brief Destructor (closes file)
+    virtual ~Memfile_LeaseMgr();
+
+    /// @brief Adds an IPv4 lease.
+    ///
+    /// @todo Not implemented yet
+    /// @param lease lease to be added
+    virtual bool addLease(const Lease4Ptr& lease);
+
+    /// @brief Adds an IPv6 lease.
+    ///
+    /// @param lease lease to be added
+    virtual bool addLease(const Lease6Ptr& lease);
+
+    /// @brief Returns existing IPv4 lease for specified IPv4 address.
+    ///
+    /// @todo Not implemented yet
+    /// @param addr address of the searched lease
+    ///
+    /// @return a collection of leases
+    virtual Lease4Ptr getLease4(isc::asiolink::IOAddress addr) const;
+
+    /// @brief Returns existing IPv4 lease for specific address and subnet
+    ///
+    /// @todo Not implemented yet
+    /// @param addr address of the searched lease
+    /// @param subnet_id ID of the subnet the lease must belong to
+    ///
+    /// @return smart pointer to the lease (or NULL if a lease is not found)
+    virtual Lease4Ptr getLease4(isc::asiolink::IOAddress addr,
+                                SubnetID subnet_id) const;
+
+    /// @brief Returns existing IPv4 leases for specified hardware address.
+    ///
+    /// @todo Not implemented yet
+    ///
+    /// Although in the usual case there will be only one lease, for mobile
+    /// clients or clients with multiple static/fixed/reserved leases there
+    /// can be more than one. Thus return type is a container, not a single
+    /// pointer.
+    ///
+    /// @param hwaddr hardware address of the client
+    ///
+    /// @return lease collection
+    virtual Lease4Collection getLease4(const HWAddr& hwaddr) const;
+
+    /// @brief Returns existing IPv4 leases for specified hardware address
+    ///        and a subnet
+    ///
+    /// @todo Not implemented yet
+    ///
+    /// There can be at most one lease for a given HW address in a single
+    /// pool, so this method with either return a single lease or NULL.
+    ///
+    /// @param hwaddr hardware address of the client
+    /// @param subnet_id identifier of the subnet that lease must belong to
+    ///
+    /// @return a pointer to the lease (or NULL if a lease is not found)
+    virtual Lease4Ptr getLease4(const HWAddr& hwaddr,
+                                SubnetID subnet_id) const;
+
+    /// @brief Returns existing IPv4 lease for specified client-id
+    ///
+    /// @todo Not implemented yet
+    ///
+    /// @param clientid client identifier
+    virtual Lease4Collection getLease4(const ClientId& clientid) const;
+
+    /// @brief Returns existing IPv4 lease for specified client-id
+    ///
+    /// There can be at most one lease for a given HW address in a single
+    /// pool, so this method with either return a single lease or NULL.
+    ///
+    /// @todo Not implemented yet
+    ///
+    /// @param clientid client identifier
+    /// @param subnet_id identifier of the subnet that lease must belong to
+    ///
+    /// @return a pointer to the lease (or NULL if a lease is not found)
+    virtual Lease4Ptr getLease4(const ClientId& clientid,
+                                SubnetID subnet_id) const;
+
+    /// @brief Returns existing IPv6 lease for a given IPv6 address.
+    ///
+    /// @param addr address of the searched lease
+    ///
+    /// @return smart pointer to the lease (or NULL if a lease is not found)
+    Lease6Ptr getLease6(const isc::asiolink::IOAddress& addr) const;
+
+    /// @brief Returns existing IPv6 lease for a given DUID+IA combination
+    ///
+    /// @todo Not implemented yet
+    ///
+    /// @param duid client DUID
+    /// @param iaid IA identifier
+    ///
+    /// @return collection of IPv6 leases
+    Lease6Collection getLease6(const DUID& duid, uint32_t iaid) const;
+
+    /// @brief Returns existing IPv6 lease for a given DUID+IA combination
+    ///
+    /// @todo Not implemented yet
+    ///
+    /// @param duid client DUID
+    /// @param iaid IA identifier
+    /// @param subnet_id identifier of the subnet the lease must belong to
+    ///
+    /// @return smart pointer to the lease (or NULL if a lease is not found)
+    Lease6Ptr getLease6(const DUID& duid, uint32_t iaid, SubnetID subnet_id) const;
+
+    /// @brief Updates IPv4 lease.
+    ///
+    /// @todo Not implemented yet
+    ///
+    /// @param lease4 The lease to be updated.
+    ///
+    /// If no such lease is present, an exception will be thrown.
+    void updateLease4(const Lease4Ptr& lease4);
+
+    /// @brief Updates IPv4 lease.
+    ///
+    /// @todo Not implemented yet
+    ///
+    /// @param lease4 The lease to be updated.
+    ///
+    /// If no such lease is present, an exception will be thrown.
+    void updateLease6(const Lease6Ptr& lease6);
+
+    /// @brief Deletes a lease.
+    ///
+    /// @todo Not implemented yet
+    ///
+    /// @param addr IPv4 address of the lease to be deleted.
+    ///
+    /// @return true if deletion was successful, false if no such lease exists
+    bool deleteLease4(uint32_t addr);
+
+    /// @brief Deletes a lease.
+    ///
+    /// @param addr IPv4 address of the lease to be deleted.
+    ///
+    /// @return true if deletion was successful, false if no such lease exists
+    bool deleteLease6(const isc::asiolink::IOAddress& addr);
+
+    /// @brief Returns backend name.
+    ///
+    /// Each backend have specific name, e.g. "mysql" or "sqlite".
+    std::string getName() const { return ("memfile"); }
+
+    /// @brief Returns description of the backend.
+    ///
+    /// This description may be multiline text that describes the backend.
+    std::string getDescription() const;
+
+    /// @brief Returns backend version.
+    std::string getVersion() const { return ("test-version"); }
+
+    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_;
+};
+
+}; // end of isc::dhcp::test namespace
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
+
+#endif // MEMFILE_LEASE_MGR_H
diff --git a/src/lib/dhcp/option6_ia.cc b/src/lib/dhcp/option6_ia.cc
index 65be711..beba75f 100644
--- a/src/lib/dhcp/option6_ia.cc
+++ b/src/lib/dhcp/option6_ia.cc
@@ -29,7 +29,7 @@ namespace isc {
 namespace dhcp {
 
 Option6IA::Option6IA(uint16_t type, uint32_t iaid)
-    :Option(Option::V6, type), iaid_(iaid) {
+    :Option(Option::V6, type), iaid_(iaid), t1_(0), t2_(0) {
 }
 
 Option6IA::Option6IA(uint16_t type, OptionBufferConstIter begin, OptionBufferConstIter end)
diff --git a/src/lib/dhcp/subnet.cc b/src/lib/dhcp/subnet.cc
index f6ce1b1..28f2391 100644
--- a/src/lib/dhcp/subnet.cc
+++ b/src/lib/dhcp/subnet.cc
@@ -15,6 +15,7 @@
 #include <dhcp/addr_utilities.h>
 #include <asiolink/io_address.h>
 #include <dhcp/subnet.h>
+#include <sstream>
 
 using namespace isc::asiolink;
 
@@ -52,6 +53,12 @@ Subnet::delOptions() {
     options_.clear();
 }
 
+std::string Subnet::toText() const {
+    std::stringstream tmp;
+    tmp << prefix_.toText() << "/" << static_cast<unsigned int>(prefix_len_);
+    return (tmp.str());
+}
+
 Subnet4::Subnet4(const isc::asiolink::IOAddress& prefix, uint8_t length,
                  const Triplet<uint32_t>& t1,
                  const Triplet<uint32_t>& t2,
diff --git a/src/lib/dhcp/subnet.h b/src/lib/dhcp/subnet.h
index a28c7cd..00b3375 100644
--- a/src/lib/dhcp/subnet.h
+++ b/src/lib/dhcp/subnet.h
@@ -284,6 +284,18 @@ public:
     /// @return unique ID for that subnet
     SubnetID getID() const { return (id_); }
 
+    /// @brief returns subnet parameters (prefix and prefix length)
+    ///
+    /// @return (prefix, prefix length) pair
+    std::pair<isc::asiolink::IOAddress, uint8_t> get() const {
+        return (std::make_pair(prefix_, prefix_len_));
+    }
+
+    /// @brief returns textual representation of the subnet (e.g. "2001:db8::/64")
+    ///
+    /// @return textual representation
+    virtual std::string toText() const;
+
 protected:
     /// @brief protected constructor
     //
diff --git a/src/lib/dhcp/tests/Makefile.am b/src/lib/dhcp/tests/Makefile.am
index fcbdec8..57055a7 100644
--- a/src/lib/dhcp/tests/Makefile.am
+++ b/src/lib/dhcp/tests/Makefile.am
@@ -48,7 +48,6 @@ 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 += memfile_lease_mgr.cc memfile_lease_mgr.h
 libdhcpsrv_unittests_SOURCES += lease_mgr_unittest.cc
 libdhcpsrv_unittests_SOURCES += alloc_engine_unittest.cc
 
@@ -60,7 +59,6 @@ libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptio
 libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
 libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
 libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcpsrv.la
-libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
 libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
 
 
diff --git a/src/lib/dhcp/tests/alloc_engine_unittest.cc b/src/lib/dhcp/tests/alloc_engine_unittest.cc
index 945c8e9..91e08d8 100644
--- a/src/lib/dhcp/tests/alloc_engine_unittest.cc
+++ b/src/lib/dhcp/tests/alloc_engine_unittest.cc
@@ -18,7 +18,7 @@
 #include <dhcp/duid.h>
 #include <dhcp/alloc_engine.h>
 #include <dhcp/cfgmgr.h>
-#include "memfile_lease_mgr.h"
+#include <dhcp/memfile_lease_mgr.h>
 #include <boost/shared_ptr.hpp>
 #include <boost/scoped_ptr.hpp>
 #include <iostream>
@@ -130,7 +130,7 @@ detailCompareLease6(const Lease6Ptr& first, const Lease6Ptr& second) {
 
 // This test checks if the simple allocation can succeed
 TEST_F(AllocEngineTest, simpleAlloc) {
-    boost::scoped_ptr<AllocEngine>(engine);
+    boost::scoped_ptr<AllocEngine> engine;
     ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
     ASSERT_TRUE(engine);
 
@@ -153,7 +153,7 @@ TEST_F(AllocEngineTest, simpleAlloc) {
 
 // This test checks if the fake allocation (for SOLICIT) can succeed
 TEST_F(AllocEngineTest, fakeAlloc) {
-    boost::scoped_ptr<AllocEngine>(engine);
+    boost::scoped_ptr<AllocEngine> engine;
     ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
     ASSERT_TRUE(engine);
 
@@ -174,7 +174,7 @@ TEST_F(AllocEngineTest, fakeAlloc) {
 // 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);
+    boost::scoped_ptr<AllocEngine> engine;
     ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
     ASSERT_TRUE(engine);
 
@@ -202,7 +202,7 @@ TEST_F(AllocEngineTest, allocWithValidHint) {
 // 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);
+    boost::scoped_ptr<AllocEngine> engine;
     ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
     ASSERT_TRUE(engine);
 
@@ -241,7 +241,7 @@ TEST_F(AllocEngineTest, allocWithUsedHint) {
 // 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);
+    boost::scoped_ptr<AllocEngine> engine;
     ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
     ASSERT_TRUE(engine);
 
diff --git a/src/lib/dhcp/tests/cfgmgr_unittest.cc b/src/lib/dhcp/tests/cfgmgr_unittest.cc
index a1ad865..5cc6345 100644
--- a/src/lib/dhcp/tests/cfgmgr_unittest.cc
+++ b/src/lib/dhcp/tests/cfgmgr_unittest.cc
@@ -32,13 +32,22 @@ using boost::scoped_ptr;
 
 namespace {
 
+class CfgMgrTest : public ::testing::Test {
+public:
+    CfgMgrTest() {
+    }
+
+    ~CfgMgrTest() {
+        CfgMgr::instance().deleteSubnets6();
+    }
+};
+
+
 // This test verifies if the configuration manager is able to hold and return
 // valid leases
-TEST(CfgMgrTest, subnet4) {
+TEST_F(CfgMgrTest, subnet4) {
     CfgMgr& cfg_mgr = CfgMgr::instance();
 
-    ASSERT_TRUE(&cfg_mgr != 0);
-
     Subnet4Ptr subnet1(new Subnet4(IOAddress("192.0.2.0"), 26, 1, 2, 3));
     Subnet4Ptr subnet2(new Subnet4(IOAddress("192.0.2.64"), 26, 1, 2, 3));
     Subnet4Ptr subnet3(new Subnet4(IOAddress("192.0.2.128"), 26, 1, 2, 3));
@@ -66,10 +75,9 @@ TEST(CfgMgrTest, subnet4) {
 
 // This test verifies if the configuration manager is able to hold and return
 // valid leases
-TEST(CfgMgrTest, DISABLED_subnet6) {
-    CfgMgr& cfg_mgr = CfgMgr::instance();
 
-    ASSERT_TRUE(&cfg_mgr != 0);
+TEST_F(CfgMgrTest, subnet6) {
+    CfgMgr& cfg_mgr = CfgMgr::instance();
 
     Subnet6Ptr subnet1(new Subnet6(IOAddress("2000::"), 48, 1, 2, 3, 4));
     Subnet6Ptr subnet2(new Subnet6(IOAddress("3000::"), 48, 1, 2, 3, 4));
@@ -83,6 +91,10 @@ TEST(CfgMgrTest, DISABLED_subnet6) {
     // Now we have only one subnet, any request will be served from it
     EXPECT_EQ(subnet1, cfg_mgr.getSubnet6(IOAddress("2000::1")));
 
+    // If we have only a single subnet and the request came from a local
+    // address, let's use that subnet
+    EXPECT_EQ(subnet1, cfg_mgr.getSubnet6(IOAddress("fe80::dead:beef")));
+
     cfg_mgr.addSubnet6(subnet2);
     cfg_mgr.addSubnet6(subnet3);
 
diff --git a/src/lib/dhcp/tests/duid_unittest.cc b/src/lib/dhcp/tests/duid_unittest.cc
index aaf6d91..435d2d2 100644
--- a/src/lib/dhcp/tests/duid_unittest.cc
+++ b/src/lib/dhcp/tests/duid_unittest.cc
@@ -166,4 +166,12 @@ TEST(ClientIdTest, operators) {
     EXPECT_TRUE(*id1 != *id3);
 }
 
+// Test checks if the toText() returns valid texual representation
+TEST(ClientIdTest, toText) {
+    uint8_t data1[] = {0, 1, 2, 3, 4, 0xff, 0xfe};
+
+    DUID duid(data1, sizeof(data1));
+    EXPECT_EQ("00:01:02:03:04:ff:fe", duid.toText());
+}
+
 } // end of anonymous namespace
diff --git a/src/lib/dhcp/tests/lease_mgr_unittest.cc b/src/lib/dhcp/tests/lease_mgr_unittest.cc
index e46bd09..66521ba 100644
--- a/src/lib/dhcp/tests/lease_mgr_unittest.cc
+++ b/src/lib/dhcp/tests/lease_mgr_unittest.cc
@@ -19,7 +19,7 @@
 #include <asiolink/io_address.h>
 #include <dhcp/lease_mgr.h>
 #include <dhcp/duid.h>
-#include "memfile_lease_mgr.h"
+#include <dhcp/memfile_lease_mgr.h>
 
 using namespace std;
 using namespace isc;
@@ -99,6 +99,27 @@ TEST_F(LeaseMgrTest, addGetDelete) {
     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")));
 
diff --git a/src/lib/dhcp/tests/memfile_lease_mgr.cc b/src/lib/dhcp/tests/memfile_lease_mgr.cc
deleted file mode 100644
index 195fd8b..0000000
--- a/src/lib/dhcp/tests/memfile_lease_mgr.cc
+++ /dev/null
@@ -1,113 +0,0 @@
-// 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 "memfile_lease_mgr.h"
-
-using namespace isc::dhcp;
-using namespace isc::dhcp::test;
-
-Memfile_LeaseMgr::Memfile_LeaseMgr(const std::string& dbconfig)
-    : LeaseMgr(dbconfig) {
-}
-
-Memfile_LeaseMgr::~Memfile_LeaseMgr() {
-}
-
-bool Memfile_LeaseMgr::addLease(const Lease4Ptr&) {
-    return (false);
-}
-
-bool Memfile_LeaseMgr::addLease(const Lease6Ptr& lease) {
-    if (getLease6(lease->addr_)) {
-        // there is a lease with specified address already
-        return (false);
-    }
-    storage6_.insert(lease);
-    return (true);
-}
-
-Lease4Ptr Memfile_LeaseMgr::getLease4(isc::asiolink::IOAddress) const {
-    return (Lease4Ptr());
-}
-
-Lease4Collection Memfile_LeaseMgr::getLease4(const HWAddr& ) const {
-    return (Lease4Collection());
-}
-
-Lease4Ptr Memfile_LeaseMgr::getLease4(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&, uint32_t,
-                                      SubnetID) const {
-
-    return (Lease6Ptr());
-}
-
-void Memfile_LeaseMgr::updateLease4(const Lease4Ptr& ) {
-}
-
-void Memfile_LeaseMgr::updateLease6(const Lease6Ptr& ) {
-
-}
-
-bool Memfile_LeaseMgr::deleteLease4(uint32_t ) {
-    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."));
-}
diff --git a/src/lib/dhcp/tests/memfile_lease_mgr.h b/src/lib/dhcp/tests/memfile_lease_mgr.h
deleted file mode 100644
index c5a41e6..0000000
--- a/src/lib/dhcp/tests/memfile_lease_mgr.h
+++ /dev/null
@@ -1,228 +0,0 @@
-// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef MEMFILE_LEASE_MGR_H
-#define MEMFILE_LEASE_MGR_H
-
-#include <dhcp/lease_mgr.h>
-#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>
-
-namespace isc {
-namespace dhcp {
-namespace test {
-
-// This is a concrete implementation of a Lease database.
-//
-// It is for testing purposes only. It is NOT a production code.
-//
-// It does not do anything useful now, and is used for abstract LeaseMgr
-// class testing. It may later evolve into more useful backend if the
-// need arises. We can reuse code from memfile benchmark. See code in
-// tests/tools/dhcp-ubench/memfile_bench.{cc|h}
-class Memfile_LeaseMgr : public LeaseMgr {
-public:
-
-    /// @brief The sole lease manager constructor
-    ///
-    /// dbconfig is a generic way of passing parameters. Parameters
-    /// are passed in the "name=value" format, separated by spaces.
-    /// Values may be enclosed in double quotes, if needed.
-    ///
-    /// @param dbconfig database configuration
-    Memfile_LeaseMgr(const std::string& dbconfig);
-
-    /// @brief Destructor (closes file)
-    virtual ~Memfile_LeaseMgr();
-
-    /// @brief Adds an IPv4 lease.
-    ///
-    /// @todo Not implemented yet
-    /// @param lease lease to be added
-    virtual bool addLease(const Lease4Ptr& lease);
-
-    /// @brief Adds an IPv6 lease.
-    ///
-    /// @param lease lease to be added
-    virtual bool addLease(const Lease6Ptr& lease);
-
-    /// @brief Returns existing IPv4 lease for specified IPv4 address.
-    ///
-    /// @todo Not implemented yet
-    /// @param addr address of the searched lease
-    ///
-    /// @return a collection of leases
-    virtual Lease4Ptr getLease4(isc::asiolink::IOAddress addr) const;
-
-    /// @brief Returns existing IPv4 lease for specific address and subnet
-    ///
-    /// @todo Not implemented yet
-    /// @param addr address of the searched lease
-    /// @param subnet_id ID of the subnet the lease must belong to
-    ///
-    /// @return smart pointer to the lease (or NULL if a lease is not found)
-    virtual Lease4Ptr getLease4(isc::asiolink::IOAddress addr,
-                                SubnetID subnet_id) const;
-
-    /// @brief Returns existing IPv4 leases for specified hardware address.
-    ///
-    /// @todo Not implemented yet
-    ///
-    /// Although in the usual case there will be only one lease, for mobile
-    /// clients or clients with multiple static/fixed/reserved leases there
-    /// can be more than one. Thus return type is a container, not a single
-    /// pointer.
-    ///
-    /// @param hwaddr hardware address of the client
-    ///
-    /// @return lease collection
-    virtual Lease4Collection getLease4(const HWAddr& hwaddr) const;
-
-    /// @brief Returns existing IPv4 leases for specified hardware address
-    ///        and a subnet
-    ///
-    /// @todo Not implemented yet
-    ///
-    /// There can be at most one lease for a given HW address in a single
-    /// pool, so this method with either return a single lease or NULL.
-    ///
-    /// @param hwaddr hardware address of the client
-    /// @param subnet_id identifier of the subnet that lease must belong to
-    ///
-    /// @return a pointer to the lease (or NULL if a lease is not found)
-    virtual Lease4Ptr getLease4(const HWAddr& hwaddr,
-                                SubnetID subnet_id) const;
-
-    /// @brief Returns existing IPv4 lease for specified client-id
-    ///
-    /// @todo Not implemented yet
-    ///
-    /// @param clientid client identifier
-    virtual Lease4Collection getLease4(const ClientId& clientid) const;
-
-    /// @brief Returns existing IPv4 lease for specified client-id
-    ///
-    /// There can be at most one lease for a given HW address in a single
-    /// pool, so this method with either return a single lease or NULL.
-    ///
-    /// @todo Not implemented yet
-    ///
-    /// @param clientid client identifier
-    /// @param subnet_id identifier of the subnet that lease must belong to
-    ///
-    /// @return a pointer to the lease (or NULL if a lease is not found)
-    virtual Lease4Ptr getLease4(const ClientId& clientid,
-                                SubnetID subnet_id) const;
-
-    /// @brief Returns existing IPv6 lease for a given IPv6 address.
-    ///
-    /// @param addr address of the searched lease
-    ///
-    /// @return smart pointer to the lease (or NULL if a lease is not found)
-    Lease6Ptr getLease6(const isc::asiolink::IOAddress& addr) const;
-
-    /// @brief Returns existing IPv6 lease for a given DUID+IA combination
-    ///
-    /// @todo Not implemented yet
-    ///
-    /// @param duid client DUID
-    /// @param iaid IA identifier
-    ///
-    /// @return collection of IPv6 leases
-    Lease6Collection getLease6(const DUID& duid, uint32_t iaid) const;
-
-    /// @brief Returns existing IPv6 lease for a given DUID+IA combination
-    ///
-    /// @todo Not implemented yet
-    ///
-    /// @param duid client DUID
-    /// @param iaid IA identifier
-    /// @param subnet_id identifier of the subnet the lease must belong to
-    ///
-    /// @return smart pointer to the lease (or NULL if a lease is not found)
-    Lease6Ptr getLease6(const DUID& duid, uint32_t iaid, SubnetID subnet_id) const;
-
-    /// @brief Updates IPv4 lease.
-    ///
-    /// @todo Not implemented yet
-    ///
-    /// @param lease4 The lease to be updated.
-    ///
-    /// If no such lease is present, an exception will be thrown.
-    void updateLease4(const Lease4Ptr& lease4);
-
-    /// @brief Updates IPv4 lease.
-    ///
-    /// @todo Not implemented yet
-    ///
-    /// @param lease4 The lease to be updated.
-    ///
-    /// If no such lease is present, an exception will be thrown.
-    void updateLease6(const Lease6Ptr& lease6);
-
-    /// @brief Deletes a lease.
-    ///
-    /// @todo Not implemented yet
-    ///
-    /// @param addr IPv4 address of the lease to be deleted.
-    ///
-    /// @return true if deletion was successful, false if no such lease exists
-    bool deleteLease4(uint32_t addr);
-
-    /// @brief Deletes a lease.
-    ///
-    /// @param addr IPv4 address of the lease to be deleted.
-    ///
-    /// @return true if deletion was successful, false if no such lease exists
-    bool deleteLease6(const isc::asiolink::IOAddress& addr);
-
-    /// @brief Returns backend name.
-    ///
-    /// Each backend have specific name, e.g. "mysql" or "sqlite".
-    std::string getName() const { return ("memfile"); }
-
-    /// @brief Returns description of the backend.
-    ///
-    /// This description may be multiline text that describes the backend.
-    std::string getDescription() const;
-
-    /// @brief Returns backend version.
-    std::string getVersion() const { return ("test-version"); }
-
-    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_;
-};
-
-}; // end of isc::dhcp::test namespace
-}; // end of isc::dhcp namespace
-}; // end of isc namespace
-
-#endif // MEMFILE_LEASE_MGR_H
diff --git a/src/lib/dhcp/tests/subnet_unittest.cc b/src/lib/dhcp/tests/subnet_unittest.cc
index be25bc1..0d9c546 100644
--- a/src/lib/dhcp/tests/subnet_unittest.cc
+++ b/src/lib/dhcp/tests/subnet_unittest.cc
@@ -158,6 +158,19 @@ TEST(Subnet4Test, inRangeinPool) {
     EXPECT_FALSE(subnet->inPool(IOAddress("192.3.0.0")));
 }
 
+// This test checks if the toText() method returns text representation
+TEST(Subnet4Test, toText) {
+    Subnet4Ptr subnet(new Subnet4(IOAddress("192.0.2.0"), 24, 1, 2, 3));
+    EXPECT_EQ("192.0.2.0/24", subnet->toText());
+}
+
+// This test checks if the get() method returns proper parameters
+TEST(Subnet4Test, get) {
+    Subnet4Ptr subnet(new Subnet4(IOAddress("192.0.2.0"), 28, 1, 2, 3));
+    EXPECT_EQ("192.0.2.0", subnet->get().first.toText());
+    EXPECT_EQ(28, subnet->get().second);
+}
+
 // Tests for Subnet6
 
 TEST(Subnet6Test, constructor) {
@@ -418,4 +431,17 @@ TEST(Subnet6Test, inRangeinPool) {
     EXPECT_FALSE(subnet->inPool(IOAddress("2001:db8::21")));
 }
 
+// This test checks if the toText() method returns text representation
+TEST(Subnet6Test, toText) {
+    Subnet6 subnet(IOAddress("2001:db8::"), 32, 1, 2, 3, 4);
+    EXPECT_EQ("2001:db8::/32", subnet.toText());
+}
+
+// This test checks if the get() method returns proper parameters
+TEST(Subnet6Test, get) {
+    Subnet6 subnet(IOAddress("2001:db8::"), 32, 1, 2, 3, 4);
+    EXPECT_EQ("2001:db8::", subnet.get().first.toText());
+    EXPECT_EQ(32, subnet.get().second);
+}
+
 };
diff --git a/tests/tools/perfdhcp/command_options.cc b/tests/tools/perfdhcp/command_options.cc
index 9aa6abf..9de07ca 100644
--- a/tests/tools/perfdhcp/command_options.cc
+++ b/tests/tools/perfdhcp/command_options.cc
@@ -23,6 +23,7 @@
 
 #include <exceptions/exceptions.h>
 #include <dhcp/iface_mgr.h>
+#include <dhcp/duid.h>
 #include "command_options.h"
 
 using namespace std;
@@ -567,8 +568,8 @@ CommandOptions::generateDuidTemplate() {
     const uint8_t duid_template_len = 14;
     duid_template_.resize(duid_template_len);
     // The first four octets consist of DUID LLT and hardware type.
-    duid_template_[0] = DUID_LLT >> 8;
-    duid_template_[1] = DUID_LLT & 0xff;
+    duid_template_[0] = static_cast<uint8_t>(static_cast<uint16_t>(isc::dhcp::DUID::DUID_LLT) >> 8);
+    duid_template_[1] = static_cast<uint8_t>(static_cast<uint16_t>(isc::dhcp::DUID::DUID_LLT) & 0xff);
     duid_template_[2] = HWTYPE_ETHERNET >> 8;
     duid_template_[3] = HWTYPE_ETHERNET & 0xff;
 



More information about the bind10-changes mailing list