Browse Source

[master] Merge branch 'trac3555' (MAC storage in Lease6, memfile)

Tomek Mrugalski 10 years ago
parent
commit
ab76a9e7a9
33 changed files with 723 additions and 210 deletions
  1. 5 4
      src/bin/dhcp4/dhcp4_srv.cc
  2. 2 4
      src/bin/dhcp4/tests/dhcp4_client.cc
  3. 18 15
      src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
  4. 6 4
      src/bin/dhcp4/tests/direct_client_unittest.cc
  5. 3 2
      src/bin/dhcp4/tests/fqdn_unittest.cc
  6. 13 2
      src/bin/dhcp6/dhcp6_srv.cc
  7. 1 0
      src/bin/dhcp6/tests/dhcp6_client.cc
  8. 4 4
      src/bin/dhcp6/tests/dhcp6_test_utils.cc
  9. 1 1
      src/bin/dhcp6/tests/fqdn_unittest.cc
  10. 10 5
      src/bin/dhcp6/tests/hooks_unittest.cc
  11. 10 10
      src/lib/dhcpsrv/alloc_engine.cc
  12. 4 2
      src/lib/dhcpsrv/alloc_engine.h
  13. 5 3
      src/lib/dhcpsrv/csv_lease_file4.cc
  14. 52 0
      src/lib/dhcpsrv/csv_lease_file6.cc
  15. 19 0
      src/lib/dhcpsrv/csv_lease_file6.h
  16. 6 0
      src/lib/dhcpsrv/dhcpsrv_messages.mes
  17. 69 17
      src/lib/dhcpsrv/lease.cc
  18. 27 13
      src/lib/dhcpsrv/lease.h
  19. 1 1
      src/lib/dhcpsrv/memfile_lease_mgr.cc
  20. 29 8
      src/lib/dhcpsrv/memfile_lease_mgr.h
  21. 10 4
      src/lib/dhcpsrv/mysql_lease_mgr.cc
  22. 12 6
      src/lib/dhcpsrv/pgsql_lease_mgr.cc
  23. 38 28
      src/lib/dhcpsrv/tests/alloc_engine_unittest.cc
  24. 12 4
      src/lib/dhcpsrv/tests/csv_lease_file4_unittest.cc
  25. 8 8
      src/lib/dhcpsrv/tests/csv_lease_file6_unittest.cc
  26. 79 35
      src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc
  27. 8 1
      src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.h
  28. 166 25
      src/lib/dhcpsrv/tests/lease_unittest.cc
  29. 60 0
      src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc
  30. 4 1
      src/lib/dhcpsrv/tests/test_utils.cc
  31. 1 0
      src/lib/util/csv_file.cc
  32. 21 3
      src/lib/util/csv_file.h
  33. 19 0
      src/lib/util/tests/csv_file_unittest.cc

+ 5 - 4
src/bin/dhcp4/dhcp4_srv.cc

@@ -489,8 +489,7 @@ Dhcpv4Srv::computeDhcid(const Lease4Ptr& lease) {
             return (D2Dhcid(lease->client_id_->getClientId(), fqdn_wire));
             return (D2Dhcid(lease->client_id_->getClientId(), fqdn_wire));
 
 
         } else {
         } else {
-            HWAddrPtr hwaddr(new HWAddr(lease->hwaddr_, HTYPE_ETHER));
-            return (D2Dhcid(hwaddr, fqdn_wire));
+            return (D2Dhcid(lease->hwaddr_, fqdn_wire));
         }
         }
     } catch (const Exception& ex) {
     } catch (const Exception& ex) {
         isc_throw(DhcidComputeError, "unable to compute DHCID: "
         isc_throw(DhcidComputeError, "unable to compute DHCID: "
@@ -1390,8 +1389,10 @@ Dhcpv4Srv::processRelease(Pkt4Ptr& release) {
         }
         }
 
 
         // Does the hardware address match? We don't want one client releasing
         // Does the hardware address match? We don't want one client releasing
-        // second client's leases.
-        if (lease->hwaddr_ != release->getHWAddr()->hwaddr_) {
+        // second client's leases. Note that we're comparing the hardware
+        // addresses only, not hardware types or sources of the hardware
+        // addresses. Thus we don't use HWAddr::equals().
+        if (lease->hwaddr_->hwaddr_ != release->getHWAddr()->hwaddr_) {
             /// @todo: Print hwaddr from lease as part of ticket #2589
             /// @todo: Print hwaddr from lease as part of ticket #2589
             LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_RELEASE_FAIL_WRONG_HWADDR)
             LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_RELEASE_FAIL_WRONG_HWADDR)
                 .arg(release->getCiaddr().toText())
                 .arg(release->getCiaddr().toText())

+ 2 - 4
src/bin/dhcp4/tests/dhcp4_client.cc

@@ -123,8 +123,7 @@ Dhcp4Client::applyConfiguration() {
 
 
     /// @todo Set the valid lifetime, t1, t2 etc.
     /// @todo Set the valid lifetime, t1, t2 etc.
     config_.lease_ = Lease4(IOAddress(context_.response_->getYiaddr()),
     config_.lease_ = Lease4(IOAddress(context_.response_->getYiaddr()),
-                            &context_.response_->getHWAddr()->hwaddr_[0],
-                            context_.response_->getHWAddr()->hwaddr_.size(),
+                            context_.response_->getHWAddr(),
                             0, 0, 0, 0, 0, time(NULL), 0, false, false,
                             0, 0, 0, 0, 0, time(NULL), 0, false, false,
                             "");
                             "");
 }
 }
@@ -132,8 +131,7 @@ Dhcp4Client::applyConfiguration() {
 void
 void
 Dhcp4Client::createLease(const asiolink::IOAddress& addr,
 Dhcp4Client::createLease(const asiolink::IOAddress& addr,
                          const uint32_t valid_lft) {
                          const uint32_t valid_lft) {
-    Lease4 lease(addr, &hwaddr_->hwaddr_[0], hwaddr_->hwaddr_.size(),
-                 0, 0, valid_lft, valid_lft / 2, valid_lft,
+    Lease4 lease(addr, hwaddr_, 0, 0, valid_lft, valid_lft / 2, valid_lft,
                  time(NULL), false, false, "");
                  time(NULL), false, false, "");
     config_.lease_ = lease;
     config_.lease_ = lease;
 }
 }

+ 18 - 15
src/bin/dhcp4/tests/dhcp4_srv_unittest.cc

@@ -816,8 +816,9 @@ TEST_F(Dhcpv4SrvTest, RenewBasic) {
     ASSERT_TRUE(subnet_->inPool(Lease::TYPE_V4, addr));
     ASSERT_TRUE(subnet_->inPool(Lease::TYPE_V4, addr));
 
 
     // let's create a lease and put it in the LeaseMgr
     // let's create a lease and put it in the LeaseMgr
-    uint8_t hwaddr2[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe};
-    Lease4Ptr used(new Lease4(IOAddress("192.0.2.106"), hwaddr2, sizeof(hwaddr2),
+    uint8_t hwaddr2_data[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe};
+    HWAddrPtr hwaddr2(new HWAddr(hwaddr2_data, sizeof(hwaddr2_data), HTYPE_ETHER));
+    Lease4Ptr used(new Lease4(IOAddress("192.0.2.106"), hwaddr2,
                               &client_id_->getDuid()[0], client_id_->getDuid().size(),
                               &client_id_->getDuid()[0], client_id_->getDuid().size(),
                               temp_valid, temp_t1, temp_t2, temp_timestamp,
                               temp_valid, temp_t1, temp_t2, temp_timestamp,
                               subnet_->getID()));
                               subnet_->getID()));
@@ -983,9 +984,9 @@ TEST_F(Dhcpv4SrvTest, ReleaseBasic) {
     ASSERT_TRUE(subnet_->inPool(Lease::TYPE_V4, addr));
     ASSERT_TRUE(subnet_->inPool(Lease::TYPE_V4, addr));
 
 
     // Let's create a lease and put it in the LeaseMgr
     // Let's create a lease and put it in the LeaseMgr
-    uint8_t mac_addr[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe};
-    HWAddrPtr hw(new HWAddr(mac_addr, sizeof(mac_addr), HTYPE_ETHER));
-    Lease4Ptr used(new Lease4(addr, mac_addr, sizeof(mac_addr),
+    uint8_t hwaddr_data[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe};
+    HWAddrPtr hwaddr(new HWAddr(hwaddr_data, sizeof(hwaddr_data), HTYPE_ETHER));
+    Lease4Ptr used(new Lease4(addr, hwaddr,
                               &client_id_->getDuid()[0], client_id_->getDuid().size(),
                               &client_id_->getDuid()[0], client_id_->getDuid().size(),
                               temp_valid, temp_t1, temp_t2, temp_timestamp,
                               temp_valid, temp_t1, temp_t2, temp_timestamp,
                               subnet_->getID()));
                               subnet_->getID()));
@@ -1002,7 +1003,7 @@ TEST_F(Dhcpv4SrvTest, ReleaseBasic) {
     rel->setCiaddr(addr);
     rel->setCiaddr(addr);
     rel->addOption(clientid);
     rel->addOption(clientid);
     rel->addOption(srv->getServerID());
     rel->addOption(srv->getServerID());
-    rel->setHWAddr(hw);
+    rel->setHWAddr(hwaddr);
     rel->setIface("eth0");
     rel->setIface("eth0");
 
 
     // Pass it to the server and hope for a REPLY
     // Pass it to the server and hope for a REPLY
@@ -1014,11 +1015,11 @@ TEST_F(Dhcpv4SrvTest, ReleaseBasic) {
     EXPECT_FALSE(l);
     EXPECT_FALSE(l);
 
 
     // Try to get the lease by hardware address
     // Try to get the lease by hardware address
-    Lease4Collection leases = LeaseMgrFactory::instance().getLease4(hw->hwaddr_);
+    Lease4Collection leases = LeaseMgrFactory::instance().getLease4(*hwaddr);
     EXPECT_EQ(leases.size(), 0);
     EXPECT_EQ(leases.size(), 0);
 
 
     // Try to get it by hw/subnet_id combination
     // Try to get it by hw/subnet_id combination
-    l = LeaseMgrFactory::instance().getLease4(hw->hwaddr_, subnet_->getID());
+    l = LeaseMgrFactory::instance().getLease4(*hwaddr, subnet_->getID());
     EXPECT_FALSE(l);
     EXPECT_FALSE(l);
 
 
     // Try by client-id
     // Try by client-id
@@ -1083,7 +1084,7 @@ TEST_F(Dhcpv4SrvTest, ReleaseReject) {
     // Let's create a lease and put it in the LeaseMgr
     // Let's create a lease and put it in the LeaseMgr
     uint8_t mac_addr[] = { 0, 0x1, 0x2, 0x3, 0x4, 0x5};
     uint8_t mac_addr[] = { 0, 0x1, 0x2, 0x3, 0x4, 0x5};
     HWAddrPtr hw(new HWAddr(mac_addr, sizeof(mac_addr), HTYPE_ETHER));
     HWAddrPtr hw(new HWAddr(mac_addr, sizeof(mac_addr), HTYPE_ETHER));
-    Lease4Ptr used(new Lease4(addr, mac_addr, sizeof(mac_addr),
+    Lease4Ptr used(new Lease4(addr, hw,
                               &client_id_->getDuid()[0], client_id_->getDuid().size(),
                               &client_id_->getDuid()[0], client_id_->getDuid().size(),
                               valid, t1, t2, timestamp, subnet_->getID()));
                               valid, t1, t2, timestamp, subnet_->getID()));
     ASSERT_TRUE(LeaseMgrFactory::instance().addLease(used));
     ASSERT_TRUE(LeaseMgrFactory::instance().addLease(used));
@@ -2613,8 +2614,9 @@ TEST_F(HooksDhcpv4SrvTest, lease4RenewSimple) {
     ASSERT_TRUE(subnet_->inPool(Lease::TYPE_V4, addr));
     ASSERT_TRUE(subnet_->inPool(Lease::TYPE_V4, addr));
 
 
     // let's create a lease and put it in the LeaseMgr
     // let's create a lease and put it in the LeaseMgr
-    uint8_t hwaddr2[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe};
-    Lease4Ptr used(new Lease4(IOAddress("192.0.2.106"), hwaddr2, sizeof(hwaddr2),
+    uint8_t hwaddr2_data[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe};
+    HWAddrPtr hwaddr2(new HWAddr(hwaddr2_data, sizeof(hwaddr2_data), HTYPE_ETHER));
+    Lease4Ptr used(new Lease4(IOAddress("192.0.2.106"), hwaddr2,
                               &client_id_->getDuid()[0], client_id_->getDuid().size(),
                               &client_id_->getDuid()[0], client_id_->getDuid().size(),
                               temp_valid, temp_t1, temp_t2, temp_timestamp,
                               temp_valid, temp_t1, temp_t2, temp_timestamp,
                               subnet_->getID()));
                               subnet_->getID()));
@@ -2700,8 +2702,9 @@ TEST_F(HooksDhcpv4SrvTest, lease4RenewSkip) {
     ASSERT_TRUE(subnet_->inPool(Lease::TYPE_V4, addr));
     ASSERT_TRUE(subnet_->inPool(Lease::TYPE_V4, addr));
 
 
     // let's create a lease and put it in the LeaseMgr
     // let's create a lease and put it in the LeaseMgr
-    uint8_t hwaddr2[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe};
-    Lease4Ptr used(new Lease4(IOAddress("192.0.2.106"), hwaddr2, sizeof(hwaddr2),
+    uint8_t hwaddr2_data[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe};
+    HWAddrPtr hwaddr2(new HWAddr(hwaddr2_data, sizeof(hwaddr2_data), HTYPE_ETHER));
+    Lease4Ptr used(new Lease4(IOAddress("192.0.2.106"), hwaddr2,
                               &client_id_->getDuid()[0], client_id_->getDuid().size(),
                               &client_id_->getDuid()[0], client_id_->getDuid().size(),
                               temp_valid, temp_t1, temp_t2, temp_timestamp,
                               temp_valid, temp_t1, temp_t2, temp_timestamp,
                               subnet_->getID()));
                               subnet_->getID()));
@@ -2769,7 +2772,7 @@ TEST_F(HooksDhcpv4SrvTest, lease4ReleaseSimple) {
     // Let's create a lease and put it in the LeaseMgr
     // Let's create a lease and put it in the LeaseMgr
     uint8_t mac_addr[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe};
     uint8_t mac_addr[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe};
     HWAddrPtr hw(new HWAddr(mac_addr, sizeof(mac_addr), HTYPE_ETHER));
     HWAddrPtr hw(new HWAddr(mac_addr, sizeof(mac_addr), HTYPE_ETHER));
-    Lease4Ptr used(new Lease4(addr, mac_addr, sizeof(mac_addr),
+    Lease4Ptr used(new Lease4(addr, hw,
                               &client_id_->getDuid()[0], client_id_->getDuid().size(),
                               &client_id_->getDuid()[0], client_id_->getDuid().size(),
                               temp_valid, temp_t1, temp_t2, temp_timestamp,
                               temp_valid, temp_t1, temp_t2, temp_timestamp,
                               subnet_->getID()));
                               subnet_->getID()));
@@ -2856,7 +2859,7 @@ TEST_F(HooksDhcpv4SrvTest, lease4ReleaseSkip) {
     // Let's create a lease and put it in the LeaseMgr
     // Let's create a lease and put it in the LeaseMgr
     uint8_t mac_addr[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe};
     uint8_t mac_addr[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe};
     HWAddrPtr hw(new HWAddr(mac_addr, sizeof(mac_addr), HTYPE_ETHER));
     HWAddrPtr hw(new HWAddr(mac_addr, sizeof(mac_addr), HTYPE_ETHER));
-    Lease4Ptr used(new Lease4(addr, mac_addr, sizeof(mac_addr),
+    Lease4Ptr used(new Lease4(addr, hw,
                               &client_id_->getDuid()[0], client_id_->getDuid().size(),
                               &client_id_->getDuid()[0], client_id_->getDuid().size(),
                               temp_valid, temp_t1, temp_t2, temp_timestamp,
                               temp_valid, temp_t1, temp_t2, temp_timestamp,
                               subnet_->getID()));
                               subnet_->getID()));

+ 6 - 4
src/bin/dhcp4/tests/direct_client_unittest.cc

@@ -307,8 +307,9 @@ TEST_F(DirectClientTest, renew) {
     // Create a lease for a client that we will later renewed. By explicitly
     // Create a lease for a client that we will later renewed. By explicitly
     // creating a lease we will get to know the lease parameters, such as
     // creating a lease we will get to know the lease parameters, such as
     // leased address etc.
     // leased address etc.
-    const uint8_t hwaddr[] = { 1, 2, 3, 4, 5, 6 };
-    Lease4Ptr lease(new Lease4(IOAddress("10.0.0.10"), hwaddr, sizeof(hwaddr),
+    const uint8_t hwaddr_data[] = { 1, 2, 3, 4, 5, 6 };
+    HWAddrPtr hwaddr(new HWAddr(hwaddr_data, sizeof(hwaddr_data), HTYPE_ETHER));
+    Lease4Ptr lease(new Lease4(IOAddress("10.0.0.10"), hwaddr,
                                &generateClientId()->getData()[0],
                                &generateClientId()->getData()[0],
                                generateClientId()->getData().size(),
                                generateClientId()->getData().size(),
                                100, 50, 75, time(NULL),
                                100, 50, 75, time(NULL),
@@ -364,8 +365,9 @@ TEST_F(DirectClientTest, rebind) {
                                                       classify_);
                                                       classify_);
     // Create a lease, which will be later renewed. By explicitly creating a
     // Create a lease, which will be later renewed. By explicitly creating a
     // lease we will know the lease parameters, such as leased address etc.
     // lease we will know the lease parameters, such as leased address etc.
-    const uint8_t hwaddr[] = { 1, 2, 3, 4, 5, 6 };
-    Lease4Ptr lease(new Lease4(IOAddress("10.0.0.10"), hwaddr, sizeof(hwaddr),
+    const uint8_t hwaddr_data[] = { 1, 2, 3, 4, 5, 6 };
+    HWAddrPtr hwaddr(new HWAddr(hwaddr_data, sizeof(hwaddr_data), HTYPE_ETHER));
+    Lease4Ptr lease(new Lease4(IOAddress("10.0.0.10"), hwaddr,
                                &generateClientId()->getData()[0],
                                &generateClientId()->getData()[0],
                                generateClientId()->getData().size(),
                                generateClientId()->getData().size(),
                                100, 50, 75, time(NULL),
                                100, 50, 75, time(NULL),

+ 3 - 2
src/bin/dhcp4/tests/fqdn_unittest.cc

@@ -93,8 +93,9 @@ public:
                           const std::string& hostname,
                           const std::string& hostname,
                           const bool fqdn_fwd,
                           const bool fqdn_fwd,
                           const bool fqdn_rev) {
                           const bool fqdn_rev) {
-        const uint8_t hwaddr[] = { 0, 1, 2, 3, 4, 5, 6 };
-        Lease4Ptr lease(new Lease4(addr, hwaddr, sizeof(hwaddr),
+        const uint8_t hwaddr_data[] = { 0, 1, 2, 3, 4, 5, 6 };
+        HWAddrPtr hwaddr(new HWAddr(hwaddr_data, sizeof(hwaddr_data), HTYPE_ETHER));
+        Lease4Ptr lease(new Lease4(addr, hwaddr,
                                    &generateClientId()->getData()[0],
                                    &generateClientId()->getData()[0],
                                    generateClientId()->getData().size(),
                                    generateClientId()->getData().size(),
                                    100, 50, 75, time(NULL), subnet_->getID()));
                                    100, 50, 75, time(NULL), subnet_->getID()));

+ 13 - 2
src/bin/dhcp6/dhcp6_srv.cc

@@ -1268,6 +1268,11 @@ Dhcpv6Srv::assignIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
         hostname = fqdn->getDomainName();
         hostname = fqdn->getDomainName();
     }
     }
 
 
+    // Attempt to get MAC address using any of available mechanisms.
+    // It's ok if there response is NULL. Hardware address is optional in Lease6
+    /// @todo: Make this configurable after trac 3554 is done.
+    HWAddrPtr hwaddr = query->getMAC(Pkt::HWADDR_SOURCE_ANY);
+
     // Use allocation engine to pick a lease for this client. Allocation engine
     // 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
     // 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
     // may be used instead. If fake_allocation is set to false, the lease will
@@ -1280,7 +1285,8 @@ Dhcpv6Srv::assignIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
                                                              hostname,
                                                              hostname,
                                                              fake_allocation,
                                                              fake_allocation,
                                                              callout_handle,
                                                              callout_handle,
-                                                             old_leases);
+                                                             old_leases,
+                                                             hwaddr);
     /// @todo: Handle more than one lease
     /// @todo: Handle more than one lease
     Lease6Ptr lease;
     Lease6Ptr lease;
     if (!leases.empty()) {
     if (!leases.empty()) {
@@ -1383,6 +1389,11 @@ Dhcpv6Srv::assignIA_PD(const Subnet6Ptr& subnet, const DuidPtr& duid,
         hint = hint_opt->getAddress();
         hint = hint_opt->getAddress();
     }
     }
 
 
+    // Attempt to get MAC address using any of available mechanisms.
+    // It's ok if there response is NULL. Hardware address is optional in Lease6
+    /// @todo: Make this configurable after trac 3554 is done.
+    HWAddrPtr hwaddr = query->getMAC(Pkt::HWADDR_SOURCE_ANY);
+
     LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_PROCESS_IA_PD_REQUEST)
     LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_PROCESS_IA_PD_REQUEST)
         .arg(duid ? duid->toText() : "(no-duid)").arg(ia->getIAID())
         .arg(duid ? duid->toText() : "(no-duid)").arg(ia->getIAID())
         .arg(hint_opt ? hint.toText() : "(no hint)");
         .arg(hint_opt ? hint.toText() : "(no hint)");
@@ -1409,7 +1420,7 @@ Dhcpv6Srv::assignIA_PD(const Subnet6Ptr& subnet, const DuidPtr& duid,
                                                              string(),
                                                              string(),
                                                              fake_allocation,
                                                              fake_allocation,
                                                              callout_handle,
                                                              callout_handle,
-                                                             old_leases);
+                                                             old_leases, hwaddr);
 
 
     if (!leases.empty()) {
     if (!leases.empty()) {
 
 

+ 1 - 0
src/bin/dhcp6/tests/dhcp6_client.cc

@@ -117,6 +117,7 @@ Dhcp6Client::applyRcvdConfiguration(const Pkt6Ptr& reply) {
                                                iaprefix->getPreferred(),
                                                iaprefix->getPreferred(),
                                                iaprefix->getValid(),
                                                iaprefix->getValid(),
                                                ia->getT1(), ia->getT2(), 0,
                                                ia->getT1(), ia->getT2(), 0,
+                                               HWAddrPtr(),
                                                iaprefix->getLength());
                                                iaprefix->getLength());
                     lease_info.lease_.cltt_ = time(NULL);
                     lease_info.lease_.cltt_ = time(NULL);
                 }
                 }

+ 4 - 4
src/bin/dhcp6/tests/dhcp6_test_utils.cc

@@ -213,7 +213,7 @@ Dhcpv6SrvTest::testRenewBasic(Lease::Type type, const std::string& existing_addr
     // Note that preferred, valid, T1 and T2 timers and CLTT are set to invalid
     // Note that preferred, valid, T1 and T2 timers and CLTT are set to invalid
     // value on purpose. They should be updated during RENEW.
     // value on purpose. They should be updated during RENEW.
     Lease6Ptr lease(new Lease6(type, existing, duid_, iaid, 501, 502, 503, 504,
     Lease6Ptr lease(new Lease6(type, existing, duid_, iaid, 501, 502, 503, 504,
-                               subnet_->getID(), prefix_len));
+                               subnet_->getID(), HWAddrPtr(), prefix_len));
     lease->cltt_ = 1234;
     lease->cltt_ = 1234;
     ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
     ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
 
 
@@ -358,7 +358,7 @@ Dhcpv6SrvTest::testRenewReject(Lease::Type type, const IOAddress& addr) {
     // value on purpose. They should be updated during RENEW.
     // value on purpose. They should be updated during RENEW.
     Lease6Ptr lease(new Lease6(type, addr, duid_, valid_iaid,
     Lease6Ptr lease(new Lease6(type, addr, duid_, valid_iaid,
                                501, 502, 503, 504, subnet_->getID(),
                                501, 502, 503, 504, subnet_->getID(),
-                               prefix_len));
+                               HWAddrPtr(), prefix_len));
     lease->cltt_ = 123; // Let's use it as an indicator that the lease
     lease->cltt_ = 123; // Let's use it as an indicator that the lease
                         // was NOT updated.
                         // was NOT updated.
     ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
     ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
@@ -430,7 +430,7 @@ Dhcpv6SrvTest::testReleaseBasic(Lease::Type type, const IOAddress& existing,
     // Let's prepopulate the database
     // Let's prepopulate the database
     Lease6Ptr lease(new Lease6(type, existing, duid_, iaid,
     Lease6Ptr lease(new Lease6(type, existing, duid_, iaid,
                                501, 502, 503, 504, subnet_->getID(),
                                501, 502, 503, 504, subnet_->getID(),
-                               prefix_len));
+                               HWAddrPtr(), prefix_len));
     ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
     ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
 
 
     // Check that the lease is really in the database
     // Check that the lease is really in the database
@@ -536,7 +536,7 @@ Dhcpv6SrvTest::testReleaseReject(Lease::Type type, const IOAddress& addr) {
     SCOPED_TRACE("CASE 2: Lease is known and belongs to this client, but to a different IAID");
     SCOPED_TRACE("CASE 2: Lease is known and belongs to this client, but to a different IAID");
 
 
     Lease6Ptr lease(new Lease6(type, addr, duid_, valid_iaid, 501, 502, 503,
     Lease6Ptr lease(new Lease6(type, addr, duid_, valid_iaid, 501, 502, 503,
-                               504, subnet_->getID(), prefix_len));
+                               504, subnet_->getID(), HWAddrPtr(), prefix_len));
     ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
     ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
 
 
     // Let's create a different RELEASE, with a bogus iaid
     // Let's create a different RELEASE, with a bogus iaid

+ 1 - 1
src/bin/dhcp6/tests/fqdn_unittest.cc

@@ -77,7 +77,7 @@ public:
         generateClientId();
         generateClientId();
         lease_.reset(new Lease6(Lease::TYPE_NA, IOAddress("2001:db8:1::1"),
         lease_.reset(new Lease6(Lease::TYPE_NA, IOAddress("2001:db8:1::1"),
                                 duid_, 1234, 501, 502, 503,
                                 duid_, 1234, 501, 502, 503,
-                                504, 1, 0));
+                                504, 1, HWAddrPtr(), 0));
         // Config DDNS to be enabled, all controls off
         // Config DDNS to be enabled, all controls off
         enableD2();
         enableD2();
     }
     }

+ 10 - 5
src/bin/dhcp6/tests/hooks_unittest.cc

@@ -1046,7 +1046,8 @@ TEST_F(HooksDhcpv6SrvTest, basic_lease6_renew) {
     // Note that preferred, valid, T1 and T2 timers and CLTT are set to invalid
     // Note that preferred, valid, T1 and T2 timers and CLTT are set to invalid
     // value on purpose. They should be updated during RENEW.
     // value on purpose. They should be updated during RENEW.
     Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, duid_, iaid,
     Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, duid_, iaid,
-                               501, 502, 503, 504, subnet_->getID(), 0));
+                               501, 502, 503, 504, subnet_->getID(),
+                               HWAddrPtr(), 0));
     lease->cltt_ = 1234;
     lease->cltt_ = 1234;
     ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
     ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
 
 
@@ -1144,7 +1145,8 @@ TEST_F(HooksDhcpv6SrvTest, leaseUpdate_lease6_renew) {
     // Note that preferred, valid, T1 and T2 timers and CLTT are set to invalid
     // Note that preferred, valid, T1 and T2 timers and CLTT are set to invalid
     // value on purpose. They should be updated during RENEW.
     // value on purpose. They should be updated during RENEW.
     Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, duid_, iaid,
     Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, duid_, iaid,
-                               501, 502, 503, 504, subnet_->getID(), 0));
+                               501, 502, 503, 504, subnet_->getID(),
+                               HWAddrPtr(), 0));
     lease->cltt_ = 1234;
     lease->cltt_ = 1234;
     ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
     ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
 
 
@@ -1236,7 +1238,8 @@ TEST_F(HooksDhcpv6SrvTest, skip_lease6_renew) {
     // Note that preferred, valid, T1 and T2 timers and CLTT are set to invalid
     // Note that preferred, valid, T1 and T2 timers and CLTT are set to invalid
     // value on purpose. They should be updated during RENEW.
     // value on purpose. They should be updated during RENEW.
     Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, duid_, iaid,
     Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, duid_, iaid,
-                               501, 502, 503, 504, subnet_->getID(), 0));
+                               501, 502, 503, 504, subnet_->getID(),
+                               HWAddrPtr(), 0));
     lease->cltt_ = 1234;
     lease->cltt_ = 1234;
     ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
     ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
 
 
@@ -1313,7 +1316,8 @@ TEST_F(HooksDhcpv6SrvTest, basic_lease6_release) {
     // Note that preferred, valid, T1 and T2 timers and CLTT are set to invalid
     // Note that preferred, valid, T1 and T2 timers and CLTT are set to invalid
     // value on purpose. They should be updated during RENEW.
     // value on purpose. They should be updated during RENEW.
     Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, duid_, iaid,
     Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, duid_, iaid,
-                               501, 502, 503, 504, subnet_->getID(), 0));
+                               501, 502, 503, 504, subnet_->getID(),
+                               HWAddrPtr(), 0));
     lease->cltt_ = 1234;
     lease->cltt_ = 1234;
     ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
     ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
 
 
@@ -1394,7 +1398,8 @@ TEST_F(HooksDhcpv6SrvTest, skip_lease6_release) {
     // Note that preferred, valid, T1 and T2 timers and CLTT are set to invalid
     // Note that preferred, valid, T1 and T2 timers and CLTT are set to invalid
     // value on purpose. They should be updated during RENEW.
     // value on purpose. They should be updated during RENEW.
     Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, duid_, iaid,
     Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, duid_, iaid,
-                               501, 502, 503, 504, subnet_->getID(), 0));
+                               501, 502, 503, 504, subnet_->getID(),
+                               HWAddrPtr(), 0));
     lease->cltt_ = 1234;
     lease->cltt_ = 1234;
     ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
     ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
 
 

+ 10 - 10
src/lib/dhcpsrv/alloc_engine.cc

@@ -299,7 +299,7 @@ AllocEngine::allocateLeases6(const Subnet6Ptr& subnet, const DuidPtr& duid,
                              const bool rev_dns_update,
                              const bool rev_dns_update,
                              const std::string& hostname, bool fake_allocation,
                              const std::string& hostname, bool fake_allocation,
                              const isc::hooks::CalloutHandlePtr& callout_handle,
                              const isc::hooks::CalloutHandlePtr& callout_handle,
-                             Lease6Collection& old_leases) {
+                             Lease6Collection& old_leases, const HWAddrPtr& hwaddr) {
 
 
     try {
     try {
         AllocatorPtr allocator = getAllocator(type);
         AllocatorPtr allocator = getAllocator(type);
@@ -349,7 +349,7 @@ AllocEngine::allocateLeases6(const Subnet6Ptr& subnet, const DuidPtr& duid,
                 lease = createLease6(subnet, duid, iaid, hint,
                 lease = createLease6(subnet, duid, iaid, hint,
                                      pool->getLength(), type,
                                      pool->getLength(), type,
                                      fwd_dns_update, rev_dns_update,
                                      fwd_dns_update, rev_dns_update,
-                                     hostname, callout_handle, fake_allocation);
+                                     hostname, hwaddr, callout_handle, fake_allocation);
 
 
                 // It can happen that the lease allocation failed (we could
                 // It can happen that the lease allocation failed (we could
                 // have lost the race condition. That means that the hint is
                 // have lost the race condition. That means that the hint is
@@ -429,7 +429,7 @@ AllocEngine::allocateLeases6(const Subnet6Ptr& subnet, const DuidPtr& duid,
 
 
                 Lease6Ptr lease = createLease6(subnet, duid, iaid, candidate,
                 Lease6Ptr lease = createLease6(subnet, duid, iaid, candidate,
                                                prefix_len, type, fwd_dns_update,
                                                prefix_len, type, fwd_dns_update,
-                                               rev_dns_update, hostname,
+                                               rev_dns_update, hostname, hwaddr,
                                                callout_handle, fake_allocation);
                                                callout_handle, fake_allocation);
                 if (lease) {
                 if (lease) {
                     // We are allocating a new lease (not renewing). So, the
                     // We are allocating a new lease (not renewing). So, the
@@ -664,7 +664,7 @@ Lease4Ptr AllocEngine::renewLease4(const SubnetPtr& subnet,
     Lease4 old_values = *lease;
     Lease4 old_values = *lease;
 
 
     lease->subnet_id_ = subnet->getID();
     lease->subnet_id_ = subnet->getID();
-    lease->hwaddr_ = hwaddr->hwaddr_;
+    lease->hwaddr_ = hwaddr;
     lease->client_id_ = clientid;
     lease->client_id_ = clientid;
     lease->cltt_ = time(NULL);
     lease->cltt_ = time(NULL);
     lease->t1_ = subnet->getT1();
     lease->t1_ = subnet->getT1();
@@ -819,7 +819,7 @@ Lease4Ptr AllocEngine::reuseExpiredLease(Lease4Ptr& expired,
 
 
     // address, lease type and prefixlen (0) stay the same
     // address, lease type and prefixlen (0) stay the same
     expired->client_id_ = clientid;
     expired->client_id_ = clientid;
-    expired->hwaddr_ = hwaddr->hwaddr_;
+    expired->hwaddr_ = hwaddr;
     expired->valid_lft_ = subnet->getValid();
     expired->valid_lft_ = subnet->getValid();
     expired->t1_ = subnet->getT1();
     expired->t1_ = subnet->getT1();
     expired->t2_ = subnet->getT2();
     expired->t2_ = subnet->getT2();
@@ -893,6 +893,7 @@ Lease6Ptr AllocEngine::createLease6(const Subnet6Ptr& subnet,
                                     const bool fwd_dns_update,
                                     const bool fwd_dns_update,
                                     const bool rev_dns_update,
                                     const bool rev_dns_update,
                                     const std::string& hostname,
                                     const std::string& hostname,
+                                    const HWAddrPtr& hwaddr,
                                     const isc::hooks::CalloutHandlePtr& callout_handle,
                                     const isc::hooks::CalloutHandlePtr& callout_handle,
                                     bool fake_allocation /*= false */ ) {
                                     bool fake_allocation /*= false */ ) {
 
 
@@ -903,7 +904,7 @@ Lease6Ptr AllocEngine::createLease6(const Subnet6Ptr& subnet,
     Lease6Ptr lease(new Lease6(type, addr, duid, iaid,
     Lease6Ptr lease(new Lease6(type, addr, duid, iaid,
                                subnet->getPreferred(), subnet->getValid(),
                                subnet->getPreferred(), subnet->getValid(),
                                subnet->getT1(), subnet->getT2(), subnet->getID(),
                                subnet->getT1(), subnet->getT2(), subnet->getID(),
-                               prefix_len));
+                               hwaddr, prefix_len));
 
 
     lease->fqdn_fwd_ = fwd_dns_update;
     lease->fqdn_fwd_ = fwd_dns_update;
     lease->fqdn_rev_ = rev_dns_update;
     lease->fqdn_rev_ = rev_dns_update;
@@ -990,10 +991,9 @@ Lease4Ptr AllocEngine::createLease4(const SubnetPtr& subnet,
         local_copy = clientid->getDuid();
         local_copy = clientid->getDuid();
     }
     }
 
 
-    Lease4Ptr lease(new Lease4(addr, &hwaddr->hwaddr_[0], hwaddr->hwaddr_.size(),
-                               &local_copy[0], local_copy.size(), subnet->getValid(),
-                               subnet->getT1(), subnet->getT2(), now,
-                               subnet->getID()));
+    Lease4Ptr lease(new Lease4(addr, hwaddr, &local_copy[0], local_copy.size(),
+                               subnet->getValid(), subnet->getT1(), subnet->getT2(),
+                               now, subnet->getID()));
 
 
     // Set FQDN specific lease parameters.
     // Set FQDN specific lease parameters.
     lease->fqdn_fwd_ = fwd_dns_update;
     lease->fqdn_fwd_ = fwd_dns_update;

+ 4 - 2
src/lib/dhcpsrv/alloc_engine.h

@@ -343,6 +343,7 @@ protected:
     ///        collection of new leases, being returned. For newly allocated
     ///        collection of new leases, being returned. For newly allocated
     ///        leases (not renewed) the NULL pointers are stored in this
     ///        leases (not renewed) the NULL pointers are stored in this
     ///        collection as old leases.
     ///        collection as old leases.
+    /// @param hwaddr Hardware address (optional, may be null for Lease6)
     ///
     ///
     /// @return Allocated IPv6 leases (may be empty if allocation failed)
     /// @return Allocated IPv6 leases (may be empty if allocation failed)
     Lease6Collection
     Lease6Collection
@@ -352,7 +353,7 @@ protected:
                     const bool fwd_dns_update, const bool rev_dns_update,
                     const bool fwd_dns_update, const bool rev_dns_update,
                     const std::string& hostname, bool fake_allocation,
                     const std::string& hostname, bool fake_allocation,
                     const isc::hooks::CalloutHandlePtr& callout_handle,
                     const isc::hooks::CalloutHandlePtr& callout_handle,
-                    Lease6Collection& old_leases);
+                    Lease6Collection& old_leases, const HWAddrPtr& hwaddr);
 
 
     /// @brief returns allocator for a given pool type
     /// @brief returns allocator for a given pool type
     /// @param type type of pool (V4, IA, TA or PD)
     /// @param type type of pool (V4, IA, TA or PD)
@@ -416,6 +417,7 @@ private:
     ///        responsibility for the reverse DNS Update for this lease
     ///        responsibility for the reverse DNS Update for this lease
     ///        (if true).
     ///        (if true).
     /// @param hostname A fully qualified domain-name of the client.
     /// @param hostname A fully qualified domain-name of the client.
+    /// @param hwaddr Hardware address (optional, may be null for Lease6)
     /// @param callout_handle a callout handle (used in hooks). A lease callouts
     /// @param callout_handle a callout handle (used in hooks). A lease callouts
     ///        will be executed if this parameter is passed (and there are callouts
     ///        will be executed if this parameter is passed (and there are callouts
     ///        registered)
     ///        registered)
@@ -427,7 +429,7 @@ private:
                            const uint32_t iaid, const isc::asiolink::IOAddress& addr,
                            const uint32_t iaid, const isc::asiolink::IOAddress& addr,
                            const uint8_t prefix_len, const Lease::Type type,
                            const uint8_t prefix_len, const Lease::Type type,
                            const bool fwd_dns_update, const bool rev_dns_update,
                            const bool fwd_dns_update, const bool rev_dns_update,
-                           const std::string& hostname,
+                           const std::string& hostname, const HWAddrPtr& hwaddr,
                            const isc::hooks::CalloutHandlePtr& callout_handle,
                            const isc::hooks::CalloutHandlePtr& callout_handle,
                            bool fake_allocation = false);
                            bool fake_allocation = false);
 
 

+ 5 - 3
src/lib/dhcpsrv/csv_lease_file4.cc

@@ -29,8 +29,10 @@ void
 CSVLeaseFile4::append(const Lease4& lease) const {
 CSVLeaseFile4::append(const Lease4& lease) const {
     CSVRow row(getColumnCount());
     CSVRow row(getColumnCount());
     row.writeAt(getColumnIndex("address"), lease.addr_.toText());
     row.writeAt(getColumnIndex("address"), lease.addr_.toText());
-    HWAddr hwaddr(lease.hwaddr_, HTYPE_ETHER);
-    row.writeAt(getColumnIndex("hwaddr"), hwaddr.toText(false));
+    if (!lease.hwaddr_) {
+        isc_throw(BadValue, "Lease4 must have hardware address specified.");
+    }
+    row.writeAt(getColumnIndex("hwaddr"), lease.hwaddr_->toText(false));
     // Client id may be unset (NULL).
     // Client id may be unset (NULL).
     if (lease.client_id_) {
     if (lease.client_id_) {
         row.writeAt(getColumnIndex("client_id"), lease.client_id_->toText());
         row.writeAt(getColumnIndex("client_id"), lease.client_id_->toText());
@@ -74,7 +76,7 @@ CSVLeaseFile4::next(Lease4Ptr& lease) {
         // that.
         // that.
         HWAddr hwaddr = readHWAddr(row);
         HWAddr hwaddr = readHWAddr(row);
         lease.reset(new Lease4(readAddress(row),
         lease.reset(new Lease4(readAddress(row),
-                               &hwaddr.hwaddr_[0], hwaddr.hwaddr_.size(),
+                               HWAddrPtr(new HWAddr(hwaddr)),
                                client_id_vec.empty() ? NULL : &client_id_vec[0],
                                client_id_vec.empty() ? NULL : &client_id_vec[0],
                                client_id_len,
                                client_id_len,
                                readValid(row),
                                readValid(row),

+ 52 - 0
src/lib/dhcpsrv/csv_lease_file6.cc

@@ -12,6 +12,7 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 // PERFORMANCE OF THIS SOFTWARE.
 
 
+#include <dhcpsrv/dhcpsrv_log.h>
 #include <dhcpsrv/csv_lease_file6.h>
 #include <dhcpsrv/csv_lease_file6.h>
 
 
 using namespace isc::asiolink;
 using namespace isc::asiolink;
@@ -41,6 +42,10 @@ CSVLeaseFile6::append(const Lease6& lease) const {
     row.writeAt(getColumnIndex("fqdn_fwd"), lease.fqdn_fwd_);
     row.writeAt(getColumnIndex("fqdn_fwd"), lease.fqdn_fwd_);
     row.writeAt(getColumnIndex("fqdn_rev"), lease.fqdn_rev_);
     row.writeAt(getColumnIndex("fqdn_rev"), lease.fqdn_rev_);
     row.writeAt(getColumnIndex("hostname"), lease.hostname_);
     row.writeAt(getColumnIndex("hostname"), lease.hostname_);
+    if (lease.hwaddr_) {
+        // We may not have hardware information
+        row.writeAt(getColumnIndex("hwaddr"), lease.hwaddr_->toText(false));
+    }
     CSVFile::append(row);
     CSVFile::append(row);
 }
 }
 
 
@@ -64,6 +69,7 @@ CSVLeaseFile6::next(Lease6Ptr& lease) {
                                readIAID(row), readPreferred(row),
                                readIAID(row), readPreferred(row),
                                readValid(row), 0, 0, // t1, t2 = 0
                                readValid(row), 0, 0, // t1, t2 = 0
                                readSubnetID(row),
                                readSubnetID(row),
+                               readHWAddr(row),
                                readPrefixLen(row)));
                                readPrefixLen(row)));
         lease->cltt_ = readCltt(row);
         lease->cltt_ = readCltt(row);
         lease->fqdn_fwd_ = readFqdnFwd(row);
         lease->fqdn_fwd_ = readFqdnFwd(row);
@@ -94,6 +100,7 @@ CSVLeaseFile6::initColumns() {
     addColumn("fqdn_fwd");
     addColumn("fqdn_fwd");
     addColumn("fqdn_rev");
     addColumn("fqdn_rev");
     addColumn("hostname");
     addColumn("hostname");
+    addColumn("hwaddr");
 }
 }
 
 
 Lease::Type
 Lease::Type
@@ -172,5 +179,50 @@ CSVLeaseFile6::readHostname(const CSVRow& row) {
     return (hostname);
     return (hostname);
 }
 }
 
 
+HWAddrPtr
+CSVLeaseFile6::readHWAddr(const CSVRow& row) {
+
+    try {
+        const HWAddr& hwaddr = HWAddr::fromText(row.readAt(getColumnIndex("hwaddr")));
+        if (hwaddr.hwaddr_.empty()) {
+            return (HWAddrPtr());
+        }
+
+        /// @todo: HWAddr returns an object, not a pointer. Without HWAddr
+        /// refactoring, at least one copy is unavoidable.
+
+        // Let's return a pointer to new freshly created copy.
+        return (HWAddrPtr(new HWAddr(hwaddr)));
+
+    } catch (const CSVFileError&) {
+        // That's ok, we may be reading old CSV file that didn't store hwaddr
+        return (HWAddrPtr());
+    } catch (const BadValue& ex) {
+        // That's worse. There was something in the file, but its conversion
+        // to HWAddr failed. Let's log it on warning and carry on.
+        LOG_WARN(dhcpsrv_logger, DHCPSRV_MEMFILE_READ_HWADDR_FAIL)
+            .arg(ex.what());
+
+        return (HWAddrPtr());
+    }
+}
+
+bool
+CSVLeaseFile6::validateHeader(const isc::util::CSVRow& header) {
+
+    if (!CSVFile::validateHeader(header)) {
+
+        // One possible validation failure is that we're reading Kea 0.9
+        // lease file that didn't have hwaddr column. Let's add it and
+        // try to revalidate.
+        isc::util::CSVRow copy = header;
+        copy.append("hwaddr");
+        return CSVFile::validateHeader(copy);
+    } else {
+        return (true);
+    }
+
+}
+
 } // end of namespace isc::dhcp
 } // end of namespace isc::dhcp
 } // end of namespace isc
 } // end of namespace isc

+ 19 - 0
src/lib/dhcpsrv/csv_lease_file6.h

@@ -77,6 +77,18 @@ public:
     /// ticket http://kea.isc.org/ticket/2405 is implemented.
     /// ticket http://kea.isc.org/ticket/2405 is implemented.
     bool next(Lease6Ptr& lease);
     bool next(Lease6Ptr& lease);
 
 
+protected:
+    /// @brief This function validates the header of the Lease6 CSV file.
+    ///
+    /// It works similar to @c CSVFile::validateHeader, but if the validation
+    /// fails, it attempts to add hwaddr column and retry validation.
+    /// That's useful when attmepting to read CSV file generated in 0.9
+    /// (did not have hwaddr field) in 0.9.1 or later code.
+    ///
+    /// @param header A row holding a header.
+    /// @return true if header matches the columns; false otherwise.
+    virtual bool validateHeader(const isc::util::CSVRow& header);
+
 private:
 private:
 
 
     /// @brief Initializes columns of the CSV file holding leases.
     /// @brief Initializes columns of the CSV file holding leases.
@@ -94,6 +106,7 @@ private:
     /// - fqdn_fwd
     /// - fqdn_fwd
     /// - fqdn_rev
     /// - fqdn_rev
     /// - hostname
     /// - hostname
+    /// - hwaddr
     void initColumns();
     void initColumns();
 
 
     ///
     ///
@@ -160,6 +173,12 @@ private:
     ///
     ///
     /// @param row CSV file holding lease values.
     /// @param row CSV file holding lease values.
     std::string readHostname(const util::CSVRow& row);
     std::string readHostname(const util::CSVRow& row);
+
+    /// @brief Reads HW address from the CSV file row.
+    ///
+    /// @param row CSV file holding lease values.
+    /// @return pointer to the HWAddr structure that was read
+    HWAddrPtr readHWAddr(const util::CSVRow& row);
     //@}
     //@}
 
 
 };
 };

+ 6 - 0
src/lib/dhcpsrv/dhcpsrv_messages.mes

@@ -313,6 +313,12 @@ testing but should not be enabled in normal circumstances. Non-persistence
 mode is enabled when 'persist4=no persist6=no' parameters are specified
 mode is enabled when 'persist4=no persist6=no' parameters are specified
 in the database access string.
 in the database access string.
 
 
+% DHCPSRV_MEMFILE_READ_HWADDR_FAIL failed to read hardware address from lease file: %1
+A warning message issued when read attempt of the hardware address stored in
+a disk file failed. The parameter should provide the exact nature of the failure.
+The database read will continue, but that particular lease will no longer
+have hardware address associated with it.
+
 % DHCPSRV_MEMFILE_ROLLBACK rolling back memory file database
 % DHCPSRV_MEMFILE_ROLLBACK rolling back memory file database
 The code has issued a rollback call.  For the memory file database, this is
 The code has issued a rollback call.  For the memory file database, this is
 a no-op.
 a no-op.

+ 69 - 17
src/lib/dhcpsrv/lease.cc

@@ -23,10 +23,10 @@ namespace dhcp {
 Lease::Lease(const isc::asiolink::IOAddress& addr, uint32_t t1, uint32_t t2,
 Lease::Lease(const isc::asiolink::IOAddress& addr, uint32_t t1, uint32_t t2,
              uint32_t valid_lft, SubnetID subnet_id, time_t cltt,
              uint32_t valid_lft, SubnetID subnet_id, time_t cltt,
              const bool fqdn_fwd, const bool fqdn_rev,
              const bool fqdn_fwd, const bool fqdn_rev,
-             const std::string& hostname)
+             const std::string& hostname, const HWAddrPtr& hwaddr)
     :addr_(addr), t1_(t1), t2_(t2), valid_lft_(valid_lft), cltt_(cltt),
     :addr_(addr), t1_(t1), t2_(t2), valid_lft_(valid_lft), cltt_(cltt),
      subnet_id_(subnet_id), fixed_(false), hostname_(hostname),
      subnet_id_(subnet_id), fixed_(false), hostname_(hostname),
-     fqdn_fwd_(fqdn_fwd), fqdn_rev_(fqdn_rev) {
+     fqdn_fwd_(fqdn_fwd), fqdn_rev_(fqdn_rev), hwaddr_(hwaddr) {
 }
 }
 
 
 std::string
 std::string
@@ -66,12 +66,19 @@ Lease::hasIdenticalFqdn(const Lease& other) const {
 Lease4::Lease4(const Lease4& other)
 Lease4::Lease4(const Lease4& other)
     : Lease(other.addr_, other.t1_, other.t2_, other.valid_lft_,
     : Lease(other.addr_, other.t1_, other.t2_, other.valid_lft_,
             other.subnet_id_, other.cltt_, other.fqdn_fwd_,
             other.subnet_id_, other.cltt_, other.fqdn_fwd_,
-            other.fqdn_rev_, other.hostname_), ext_(other.ext_),
-      hwaddr_(other.hwaddr_) {
+            other.fqdn_rev_, other.hostname_, other.hwaddr_),
+            ext_(other.ext_) {
 
 
     fixed_ = other.fixed_;
     fixed_ = other.fixed_;
     comments_ = other.comments_;
     comments_ = other.comments_;
 
 
+    // Copy the hardware address if it is defined.
+    if (other.hwaddr_) {
+        hwaddr_.reset(new HWAddr(*other.hwaddr_));
+    } else {
+        hwaddr_.reset();
+    }
+
     if (other.client_id_) {
     if (other.client_id_) {
         client_id_.reset(new ClientId(other.client_id_->getClientId()));
         client_id_.reset(new ClientId(other.client_id_->getClientId()));
 
 
@@ -91,6 +98,15 @@ Lease4::getClientIdVector() const {
     return (client_id_->getClientId());
     return (client_id_->getClientId());
 }
 }
 
 
+const std::vector<uint8_t>&
+Lease::getHWAddrVector() const {
+    if (!hwaddr_) {
+        static std::vector<uint8_t> empty_vec;
+        return (empty_vec);
+    }
+    return (hwaddr_->hwaddr_);
+}
+
 bool
 bool
 Lease4::matches(const Lease4& other) const {
 Lease4::matches(const Lease4& other) const {
     if ((client_id_ && !other.client_id_) ||
     if ((client_id_ && !other.client_id_) ||
@@ -105,10 +121,22 @@ Lease4::matches(const Lease4& other) const {
         return false;
         return false;
     }
     }
 
 
+    // Note that hwaddr_ is now a poiner to the HWAddr structure.
+    // We can't simply compare smart pointers, we need to compare the
+    // actual objects they point to. If one of the leases had a hardware address
+    // and the other doesn't, they clearly don't match.
+    if ( (!hwaddr_ && other.hwaddr_) ||
+         (hwaddr_ && !other.hwaddr_) ) {
+        return (false);
+    }
+
+    // The lease is equal if addresses and extensions match and there is
+    // either the same hardware address on both or neither have hardware address
+    // specified.
     return (addr_ == other.addr_ &&
     return (addr_ == other.addr_ &&
             ext_ == other.ext_ &&
             ext_ == other.ext_ &&
-            hwaddr_ == other.hwaddr_);
-
+            (!hwaddr_ ||
+             *hwaddr_ == *other.hwaddr_) );
 }
 }
 
 
 Lease4&
 Lease4&
@@ -126,7 +154,13 @@ Lease4::operator=(const Lease4& other) {
         fqdn_rev_ = other.fqdn_rev_;
         fqdn_rev_ = other.fqdn_rev_;
         comments_ = other.comments_;
         comments_ = other.comments_;
         ext_ = other.ext_;
         ext_ = other.ext_;
-        hwaddr_ = other.hwaddr_;
+
+        // Copy the hardware address if it is defined.
+        if (other.hwaddr_) {
+            hwaddr_.reset(new HWAddr(*other.hwaddr_));
+        } else {
+            hwaddr_.reset();
+        }
 
 
         if (other.client_id_) {
         if (other.client_id_) {
             client_id_.reset(new ClientId(other.client_id_->getClientId()));
             client_id_.reset(new ClientId(other.client_id_->getClientId()));
@@ -139,12 +173,13 @@ Lease4::operator=(const Lease4& other) {
 
 
 Lease6::Lease6(Type type, const isc::asiolink::IOAddress& addr,
 Lease6::Lease6(Type type, const isc::asiolink::IOAddress& addr,
                DuidPtr duid, uint32_t iaid, uint32_t preferred, uint32_t valid,
                DuidPtr duid, uint32_t iaid, uint32_t preferred, uint32_t valid,
-               uint32_t t1, uint32_t t2, SubnetID subnet_id, uint8_t prefixlen)
-    : Lease(addr, t1, t2, valid, subnet_id, 0/*cltt*/, false, false, ""),
+               uint32_t t1, uint32_t t2, SubnetID subnet_id,
+               const HWAddrPtr& hwaddr, uint8_t prefixlen)
+    : Lease(addr, t1, t2, valid, subnet_id, 0/*cltt*/, false, false, "", hwaddr),
       type_(type), prefixlen_(prefixlen), iaid_(iaid), duid_(duid),
       type_(type), prefixlen_(prefixlen), iaid_(iaid), duid_(duid),
       preferred_lft_(preferred) {
       preferred_lft_(preferred) {
     if (!duid) {
     if (!duid) {
-        isc_throw(InvalidOperation, "DUID must be specified for a lease");
+        isc_throw(InvalidOperation, "DUID is mandatory for an IPv6 lease");
     }
     }
 
 
     cltt_ = time(NULL);
     cltt_ = time(NULL);
@@ -154,22 +189,23 @@ Lease6::Lease6(Type type, const isc::asiolink::IOAddress& addr,
                DuidPtr duid, uint32_t iaid, uint32_t preferred, uint32_t valid,
                DuidPtr duid, uint32_t iaid, uint32_t preferred, uint32_t valid,
                uint32_t t1, uint32_t t2, SubnetID subnet_id,
                uint32_t t1, uint32_t t2, SubnetID subnet_id,
                const bool fqdn_fwd, const bool fqdn_rev,
                const bool fqdn_fwd, const bool fqdn_rev,
-               const std::string& hostname, uint8_t prefixlen)
+               const std::string& hostname, const HWAddrPtr& hwaddr,
+               uint8_t prefixlen)
     : Lease(addr, t1, t2, valid, subnet_id, 0/*cltt*/,
     : Lease(addr, t1, t2, valid, subnet_id, 0/*cltt*/,
-            fqdn_fwd, fqdn_rev, hostname),
+            fqdn_fwd, fqdn_rev, hostname, hwaddr),
       type_(type), prefixlen_(prefixlen), iaid_(iaid), duid_(duid),
       type_(type), prefixlen_(prefixlen), iaid_(iaid), duid_(duid),
       preferred_lft_(preferred) {
       preferred_lft_(preferred) {
     if (!duid) {
     if (!duid) {
-        isc_throw(InvalidOperation, "DUID must be specified for a lease");
+        isc_throw(InvalidOperation, "DUID is mandatory for an IPv6 lease");
     }
     }
 
 
     cltt_ = time(NULL);
     cltt_ = time(NULL);
 }
 }
 
 
 Lease6::Lease6()
 Lease6::Lease6()
-    : Lease(isc::asiolink::IOAddress("::"), 0, 0, 0, 0, 0, false, false, ""),
-      type_(TYPE_NA), prefixlen_(0), iaid_(0), duid_(DuidPtr()),
-      preferred_lft_(0) {
+    : Lease(isc::asiolink::IOAddress("::"), 0, 0, 0, 0, 0, false, false, "",
+            HWAddrPtr()), type_(TYPE_NA), prefixlen_(0), iaid_(0),
+            duid_(DuidPtr()), preferred_lft_(0) {
 }
 }
 
 
 const std::vector<uint8_t>&
 const std::vector<uint8_t>&
@@ -186,14 +222,16 @@ std::string
 Lease6::toText() const {
 Lease6::toText() const {
     ostringstream stream;
     ostringstream stream;
 
 
+    /// @todo: print out DUID
     stream << "Type:          " << typeToText(type_) << "("
     stream << "Type:          " << typeToText(type_) << "("
-           << static_cast<int>(type_) << ") ";
+           << static_cast<int>(type_) << ")\n";
     stream << "Address:       " << addr_ << "\n"
     stream << "Address:       " << addr_ << "\n"
            << "Prefix length: " << static_cast<int>(prefixlen_) << "\n"
            << "Prefix length: " << static_cast<int>(prefixlen_) << "\n"
            << "IAID:          " << iaid_ << "\n"
            << "IAID:          " << iaid_ << "\n"
            << "Pref life:     " << preferred_lft_ << "\n"
            << "Pref life:     " << preferred_lft_ << "\n"
            << "Valid life:    " << valid_lft_ << "\n"
            << "Valid life:    " << valid_lft_ << "\n"
            << "Cltt:          " << cltt_ << "\n"
            << "Cltt:          " << cltt_ << "\n"
+           << "Hardware addr: " << (hwaddr_?hwaddr_->toText(false):"(none)") << "\n"
            << "Subnet ID:     " << subnet_id_ << "\n";
            << "Subnet ID:     " << subnet_id_ << "\n";
 
 
     return (stream.str());
     return (stream.str());
@@ -203,11 +241,13 @@ std::string
 Lease4::toText() const {
 Lease4::toText() const {
     ostringstream stream;
     ostringstream stream;
 
 
+    /// @todo: print out client-id (if present)
     stream << "Address:       " << addr_ << "\n"
     stream << "Address:       " << addr_ << "\n"
            << "Valid life:    " << valid_lft_ << "\n"
            << "Valid life:    " << valid_lft_ << "\n"
            << "T1:            " << t1_ << "\n"
            << "T1:            " << t1_ << "\n"
            << "T2:            " << t2_ << "\n"
            << "T2:            " << t2_ << "\n"
            << "Cltt:          " << cltt_ << "\n"
            << "Cltt:          " << cltt_ << "\n"
+           << "Hardware addr: " << (hwaddr_?hwaddr_->toText(false):"(none)") << "\n"
            << "Subnet ID:     " << subnet_id_ << "\n";
            << "Subnet ID:     " << subnet_id_ << "\n";
 
 
     return (stream.str());
     return (stream.str());
@@ -243,6 +283,18 @@ Lease4::operator==(const Lease4& other) const {
 
 
 bool
 bool
 Lease6::matches(const Lease6& other) const {
 Lease6::matches(const Lease6& other) const {
+
+    // One lease has a hardware address, the other doesn't.
+    if ( (!hwaddr_ && other.hwaddr_) ||
+         (hwaddr_ && !other.hwaddr_) ) {
+        return (false);
+    }
+
+    // Both leases have hardware address, but they are not equal.
+    if (hwaddr_ && (*hwaddr_ != *other.hwaddr_)) {
+        return (false);
+    }
+
     return (addr_ == other.addr_ &&
     return (addr_ == other.addr_ &&
             type_ == other.type_ &&
             type_ == other.type_ &&
             prefixlen_ == other.prefixlen_ &&
             prefixlen_ == other.prefixlen_ &&

+ 27 - 13
src/lib/dhcpsrv/lease.h

@@ -59,10 +59,12 @@ struct Lease {
     /// @param fqdn_fwd If true, forward DNS update is performed for a lease.
     /// @param fqdn_fwd If true, forward DNS update is performed for a lease.
     /// @param fqdn_rev If true, reverse DNS update is performed for a lease.
     /// @param fqdn_rev If true, reverse DNS update is performed for a lease.
     /// @param hostname FQDN of the client which gets the lease.
     /// @param hostname FQDN of the client which gets the lease.
+    /// @param hwaddr Hardware/MAC address
     Lease(const isc::asiolink::IOAddress& addr, uint32_t t1, uint32_t t2,
     Lease(const isc::asiolink::IOAddress& addr, uint32_t t1, uint32_t t2,
           uint32_t valid_lft, SubnetID subnet_id, time_t cltt,
           uint32_t valid_lft, SubnetID subnet_id, time_t cltt,
           const bool fqdn_fwd, const bool fqdn_rev,
           const bool fqdn_fwd, const bool fqdn_rev,
-          const std::string& hostname);
+          const std::string& hostname,
+          const HWAddrPtr& hwaddr);
 
 
     /// @brief Destructor
     /// @brief Destructor
     virtual ~Lease() {}
     virtual ~Lease() {}
@@ -126,6 +128,11 @@ struct Lease {
     /// Set true if the DNS PTR record for this lease has been updated.
     /// Set true if the DNS PTR record for this lease has been updated.
     bool fqdn_rev_;
     bool fqdn_rev_;
 
 
+    /// @brief Client's MAC/hardware address
+    ///
+    /// This information may not be available in certain cases.
+    HWAddrPtr hwaddr_;
+
     /// @brief Lease comments
     /// @brief Lease comments
     ///
     ///
     /// Currently not used. It may be used for keeping comments made by the
     /// Currently not used. It may be used for keeping comments made by the
@@ -149,6 +156,15 @@ struct Lease {
     /// lease is equal to the FQDN data of our lease (true) or not (false).
     /// lease is equal to the FQDN data of our lease (true) or not (false).
     bool hasIdenticalFqdn(const Lease& other) const;
     bool hasIdenticalFqdn(const Lease& other) const;
 
 
+    /// @brief Returns raw (as vector) hardware address
+    ///
+    /// This method is needed in multi-index container as key extractor.
+    /// The const reference is only valid as long as the object that returned it.
+    /// In the unlikely case when Lease4 does not have a hardware address,
+    /// the function will return an empty vector.
+    ///
+    /// @return const reference to the hardware address
+    const std::vector<uint8_t>& getHWAddrVector() const;
 };
 };
 
 
 /// @brief Structure that holds a lease for IPv4 address
 /// @brief Structure that holds a lease for IPv4 address
@@ -169,9 +185,6 @@ struct Lease4 : public Lease {
     /// should be 0.
     /// should be 0.
     uint32_t ext_;
     uint32_t ext_;
 
 
-    /// @brief Hardware address
-    std::vector<uint8_t> hwaddr_;
-
     /// @brief Client identifier
     /// @brief Client identifier
     ///
     ///
     /// @todo Should this be a pointer to a client ID or the ID itself?
     /// @todo Should this be a pointer to a client ID or the ID itself?
@@ -181,8 +194,7 @@ struct Lease4 : public Lease {
     /// @brief Constructor
     /// @brief Constructor
     ///
     ///
     /// @param addr IPv4 address.
     /// @param addr IPv4 address.
-    /// @param hwaddr Hardware address buffer
-    /// @param hwaddr_len Length of hardware address buffer
+    /// @param hwaddr A pointer to HWAddr structure
     /// @param clientid Client identification buffer
     /// @param clientid Client identification buffer
     /// @param clientid_len Length of client identification buffer
     /// @param clientid_len Length of client identification buffer
     /// @param valid_lft Lifetime of the lease
     /// @param valid_lft Lifetime of the lease
@@ -193,14 +205,13 @@ struct Lease4 : public Lease {
     /// @param fqdn_fwd If true, forward DNS update is performed for a lease.
     /// @param fqdn_fwd If true, forward DNS update is performed for a lease.
     /// @param fqdn_rev If true, reverse DNS update is performed for a lease.
     /// @param fqdn_rev If true, reverse DNS update is performed for a lease.
     /// @param hostname FQDN of the client which gets the lease.
     /// @param hostname FQDN of the client which gets the lease.
-    Lease4(const isc::asiolink::IOAddress& addr, const uint8_t* hwaddr, size_t hwaddr_len,
+    Lease4(const isc::asiolink::IOAddress& addr, const HWAddrPtr& hwaddr,
            const uint8_t* clientid, size_t clientid_len, uint32_t valid_lft,
            const uint8_t* clientid, size_t clientid_len, uint32_t valid_lft,
            uint32_t t1, uint32_t t2, time_t cltt, uint32_t subnet_id,
            uint32_t t1, uint32_t t2, time_t cltt, uint32_t subnet_id,
            const bool fqdn_fwd = false, const bool fqdn_rev = false,
            const bool fqdn_fwd = false, const bool fqdn_rev = false,
            const std::string& hostname = "")
            const std::string& hostname = "")
         : Lease(addr, t1, t2, valid_lft, subnet_id, cltt, fqdn_fwd, fqdn_rev,
         : Lease(addr, t1, t2, valid_lft, subnet_id, cltt, fqdn_fwd, fqdn_rev,
-                hostname),
-        ext_(0), hwaddr_(hwaddr, hwaddr + hwaddr_len) {
+                hostname, hwaddr), ext_(0) {
         if (clientid_len) {
         if (clientid_len) {
             client_id_.reset(new ClientId(clientid, clientid_len));
             client_id_.reset(new ClientId(clientid, clientid_len));
         }
         }
@@ -209,7 +220,7 @@ struct Lease4 : public Lease {
     /// @brief Default constructor
     /// @brief Default constructor
     ///
     ///
     /// Initialize fields that don't have a default constructor.
     /// Initialize fields that don't have a default constructor.
-    Lease4() : Lease(0, 0, 0, 0, 0, 0, false, false, "") {
+    Lease4() : Lease(0, 0, 0, 0, 0, 0, false, false, "", HWAddrPtr()) {
     }
     }
 
 
     /// @brief Copy constructor
     /// @brief Copy constructor
@@ -319,10 +330,12 @@ struct Lease6 : public Lease {
     /// @param t1 A value of the T1 timer.
     /// @param t1 A value of the T1 timer.
     /// @param t2 A value of the T2 timer.
     /// @param t2 A value of the T2 timer.
     /// @param subnet_id A Subnet identifier.
     /// @param subnet_id A Subnet identifier.
-    /// @param prefixlen An address prefix length.
+    /// @param hwaddr hardware/MAC address (optional)
+    /// @param prefixlen An address prefix length (optional, defaults to 128)
     Lease6(Type type, const isc::asiolink::IOAddress& addr, DuidPtr duid,
     Lease6(Type type, const isc::asiolink::IOAddress& addr, DuidPtr duid,
            uint32_t iaid, uint32_t preferred, uint32_t valid, uint32_t t1,
            uint32_t iaid, uint32_t preferred, uint32_t valid, uint32_t t1,
-           uint32_t t2, SubnetID subnet_id, uint8_t prefixlen = 128);
+           uint32_t t2, SubnetID subnet_id, const HWAddrPtr& hwaddr = HWAddrPtr(),
+           uint8_t prefixlen = 128);
 
 
     /// @brief Constructor, including FQDN data.
     /// @brief Constructor, including FQDN data.
     ///
     ///
@@ -338,12 +351,13 @@ struct Lease6 : public Lease {
     /// @param fqdn_fwd If true, forward DNS update is performed for a lease.
     /// @param fqdn_fwd If true, forward DNS update is performed for a lease.
     /// @param fqdn_rev If true, reverse DNS update is performed for a lease.
     /// @param fqdn_rev If true, reverse DNS update is performed for a lease.
     /// @param hostname FQDN of the client which gets the lease.
     /// @param hostname FQDN of the client which gets the lease.
+    /// @param hwaddr hardware address (MAC), may be NULL
     /// @param prefixlen An address prefix length.
     /// @param prefixlen An address prefix length.
     Lease6(Type type, const isc::asiolink::IOAddress& addr, DuidPtr duid,
     Lease6(Type type, const isc::asiolink::IOAddress& addr, DuidPtr duid,
            uint32_t iaid, uint32_t preferred, uint32_t valid, uint32_t t1,
            uint32_t iaid, uint32_t preferred, uint32_t valid, uint32_t t1,
            uint32_t t2, SubnetID subnet_id, const bool fqdn_fwd,
            uint32_t t2, SubnetID subnet_id, const bool fqdn_fwd,
            const bool fqdn_rev, const std::string& hostname,
            const bool fqdn_rev, const std::string& hostname,
-           uint8_t prefixlen = 0);
+           const HWAddrPtr& hwaddr = HWAddrPtr(), uint8_t prefixlen = 0);
 
 
     /// @brief Constructor
     /// @brief Constructor
     ///
     ///

+ 1 - 1
src/lib/dhcpsrv/memfile_lease_mgr.cc

@@ -129,7 +129,7 @@ Memfile_LeaseMgr::getLease4(const HWAddr& hwaddr) const {
         lease != idx.end(); ++lease) {
         lease != idx.end(); ++lease) {
 
 
         // Every Lease4 has a hardware address, so we can compare it
         // Every Lease4 has a hardware address, so we can compare it
-        if ((*lease)->hwaddr_ == hwaddr.hwaddr_) {
+        if ( (*(*lease)->hwaddr_) == hwaddr) {
             collection.push_back((*lease));
             collection.push_back((*lease));
         }
         }
     }
     }

+ 29 - 8
src/lib/dhcpsrv/memfile_lease_mgr.h

@@ -71,6 +71,23 @@ namespace dhcp {
 class Memfile_LeaseMgr : public LeaseMgr {
 class Memfile_LeaseMgr : public LeaseMgr {
 public:
 public:
 
 
+    /// @defgroup versions Specified memfile backend version.
+    ///
+    /// @brief Defines major version of the memfile backend.
+    ///
+    /// Version history:
+    /// 1.0 - initial version (released in Kea 0.9)
+    /// 2.0 - hwaddr column added (to be released in Kea 0.9.1)
+    ///
+    /// @{
+    static const int MAJOR_VERSION = 2;
+
+    /// Defines minor version of the memfile backend.
+    static const int MINOR_VERSION = 0;
+
+    /// @}
+
+
     /// @brief Specifies universe (V4, V6)
     /// @brief Specifies universe (V4, V6)
     ///
     ///
     /// This enumeration is used by various functions in Memfile Lease Manager,
     /// This enumeration is used by various functions in Memfile Lease Manager,
@@ -275,7 +292,7 @@ public:
     /// @return Version number as a pair of unsigned integers.  "first" is the
     /// @return Version number as a pair of unsigned integers.  "first" is the
     ///         major version number, "second" the minor number.
     ///         major version number, "second" the minor number.
     virtual std::pair<uint32_t, uint32_t> getVersion() const {
     virtual std::pair<uint32_t, uint32_t> getVersion() const {
-        return (std::make_pair(1, 0));
+        return (std::make_pair(MAJOR_VERSION, MINOR_VERSION));
     }
     }
 
 
     /// @brief Commit Transactions
     /// @brief Commit Transactions
@@ -434,9 +451,11 @@ protected:
                 boost::multi_index::composite_key<
                 boost::multi_index::composite_key<
                     Lease4,
                     Lease4,
                     // The hardware address is held in the hwaddr_ member of the
                     // The hardware address is held in the hwaddr_ member of the
-                    // Lease4 object.
-                    boost::multi_index::member<Lease4, std::vector<uint8_t>,
-                                               &Lease4::hwaddr_>,
+                    // Lease4 object, which is a HWAddr object. Boost does not
+                    // provide a key extractor for getting a member of a member,
+                    // so we need a simple method for that.
+                    boost::multi_index::const_mem_fun<Lease, const std::vector<uint8_t>&,
+                                               &Lease::getHWAddrVector>,
                     // The subnet id is held in the subnet_id_ member of Lease4
                     // The subnet id is held in the subnet_id_ member of Lease4
                     // class. Note that the subnet_id_ is defined in the base
                     // class. Note that the subnet_id_ is defined in the base
                     // class (Lease) so we have to point to this class rather
                     // class (Lease) so we have to point to this class rather
@@ -470,10 +489,12 @@ protected:
                     // calling getClientIdVector const function.
                     // calling getClientIdVector const function.
                     boost::multi_index::const_mem_fun<Lease4, const std::vector<uint8_t>&,
                     boost::multi_index::const_mem_fun<Lease4, const std::vector<uint8_t>&,
                                                       &Lease4::getClientIdVector>,
                                                       &Lease4::getClientIdVector>,
-                    // The hardware address is held in the hwaddr_ member of the
-                    // Lease4 object.
-                    boost::multi_index::member<Lease4, std::vector<uint8_t>,
-                                               &Lease4::hwaddr_>,
+                    // The hardware address is held in the hwaddr_ object. We can
+                    // access the raw data using lease->hwaddr_->hwaddr_, but Boost
+                    // doesn't seem to provide a way to use member of a member for this,
+                    // so we need a simple key extractor method (getRawHWAddr).
+                    boost::multi_index::const_mem_fun<Lease, const std::vector<uint8_t>&,
+                                            &Lease::getHWAddrVector>,
                     // The subnet id is accessed through the subnet_id_ member.
                     // The subnet id is accessed through the subnet_id_ member.
                     boost::multi_index::member<Lease, SubnetID, &Lease::subnet_id_>
                     boost::multi_index::member<Lease, SubnetID, &Lease::subnet_id_>
                 >
                 >

+ 10 - 4
src/lib/dhcpsrv/mysql_lease_mgr.cc

@@ -358,9 +358,9 @@ public:
         // hwaddr: varbinary(128)
         // hwaddr: varbinary(128)
         // For speed, we avoid copying the data into temporary storage and
         // For speed, we avoid copying the data into temporary storage and
         // instead extract it from the lease structure directly.
         // instead extract it from the lease structure directly.
-        hwaddr_length_ = lease_->hwaddr_.size();
+        hwaddr_length_ = lease_->hwaddr_->hwaddr_.size();
         bind_[1].buffer_type = MYSQL_TYPE_BLOB;
         bind_[1].buffer_type = MYSQL_TYPE_BLOB;
-        bind_[1].buffer = reinterpret_cast<char*>(&(lease_->hwaddr_[0]));
+        bind_[1].buffer = reinterpret_cast<char*>(&(lease_->hwaddr_->hwaddr_[0]));
         bind_[1].buffer_length = hwaddr_length_;
         bind_[1].buffer_length = hwaddr_length_;
         bind_[1].length = &hwaddr_length_;
         bind_[1].length = &hwaddr_length_;
         // bind_[1].is_null = &MLM_FALSE; // commented out for performance
         // bind_[1].is_null = &MLM_FALSE; // commented out for performance
@@ -573,8 +573,11 @@ public:
         std::string hostname(hostname_buffer_,
         std::string hostname(hostname_buffer_,
                              hostname_buffer_ + hostname_length_);
                              hostname_buffer_ + hostname_length_);
 
 
+        // Recreate the hardware address.
+        HWAddrPtr hwaddr(new HWAddr(hwaddr_buffer_, hwaddr_length_, HTYPE_ETHER));
+
         // note that T1 and T2 are not stored
         // note that T1 and T2 are not stored
-        return (Lease4Ptr(new Lease4(addr4_, hwaddr_buffer_, hwaddr_length_,
+        return (Lease4Ptr(new Lease4(addr4_, hwaddr,
                                      client_id_buffer_, client_id_length_,
                                      client_id_buffer_, client_id_length_,
                                      valid_lifetime_, 0, 0, cltt, subnet_id_,
                                      valid_lifetime_, 0, 0, cltt, subnet_id_,
                                      fqdn_fwd_, fqdn_rev_, hostname)));
                                      fqdn_fwd_, fqdn_rev_, hostname)));
@@ -998,12 +1001,15 @@ public:
         std::string hostname(hostname_buffer_,
         std::string hostname(hostname_buffer_,
                              hostname_buffer_ + hostname_length_);
                              hostname_buffer_ + hostname_length_);
 
 
+        /// @todo: HWAddr is not yet stored, see ticket #3556.
+        HWAddrPtr hwaddr;
+
         // Create the lease and set the cltt (after converting from the
         // Create the lease and set the cltt (after converting from the
         // expire time retrieved from the database).
         // expire time retrieved from the database).
         Lease6Ptr result(new Lease6(type, addr, duid_ptr, iaid_,
         Lease6Ptr result(new Lease6(type, addr, duid_ptr, iaid_,
                                     pref_lifetime_, valid_lifetime_, 0, 0,
                                     pref_lifetime_, valid_lifetime_, 0, 0,
                                     subnet_id_, fqdn_fwd_, fqdn_rev_,
                                     subnet_id_, fqdn_fwd_, fqdn_rev_,
-                                    hostname, prefixlen_));
+                                    hostname, hwaddr, prefixlen_));
         time_t cltt = 0;
         time_t cltt = 0;
         MySqlLeaseMgr::convertFromDatabaseTime(expire_, valid_lifetime_, cltt);
         MySqlLeaseMgr::convertFromDatabaseTime(expire_, valid_lifetime_, cltt);
         result->cltt_ = cltt;
         result->cltt_ = cltt;

+ 12 - 6
src/lib/dhcpsrv/pgsql_lease_mgr.cc

@@ -607,17 +607,17 @@ public:
                         (static_cast<uint32_t>(lease->addr_));
                         (static_cast<uint32_t>(lease->addr_));
             bind_array.add(addr_str_);
             bind_array.add(addr_str_);
 
 
-            if (!lease->hwaddr_.empty()) {
+            if (lease->hwaddr_ && !lease->hwaddr_->hwaddr_.empty()) {
                 // PostgreSql does not provide MAX on variable length types
                 // PostgreSql does not provide MAX on variable length types
                 // so we have to enforce it ourselves.
                 // so we have to enforce it ourselves.
-                if (lease->hwaddr_.size() > HWAddr::MAX_HWADDR_LEN) {
+                if (lease->hwaddr_->hwaddr_.size() > HWAddr::MAX_HWADDR_LEN) {
                         isc_throw(DbOperationError, "Hardware address length : "
                         isc_throw(DbOperationError, "Hardware address length : "
-                                  << lease_->hwaddr_.size()
+                                  << lease_->hwaddr_->hwaddr_.size()
                                   << " exceeds maximum allowed of: "
                                   << " exceeds maximum allowed of: "
                                   << HWAddr::MAX_HWADDR_LEN);
                                   << HWAddr::MAX_HWADDR_LEN);
                 }
                 }
 
 
-                bind_array.add(lease->hwaddr_);
+                bind_array.add(lease->hwaddr_->hwaddr_);
             } else {
             } else {
                 bind_array.add("");
                 bind_array.add("");
             }
             }
@@ -683,7 +683,10 @@ public:
 
 
             hostname_ = getRawColumnValue(r, row, HOSTNAME_COL);
             hostname_ = getRawColumnValue(r, row, HOSTNAME_COL);
 
 
-            return (Lease4Ptr(new Lease4(addr4_, hwaddr_buffer_, hwaddr_length_,
+            HWAddrPtr hwaddr(new HWAddr(hwaddr_buffer_, hwaddr_length_,
+                                        HTYPE_ETHER));
+
+            return (Lease4Ptr(new Lease4(addr4_, hwaddr,
                                          client_id_buffer_, client_id_length_,
                                          client_id_buffer_, client_id_length_,
                                          valid_lifetime_, 0, 0, cltt_,
                                          valid_lifetime_, 0, 0, cltt_,
                                          subnet_id_, fqdn_fwd_, fqdn_rev_,
                                          subnet_id_, fqdn_fwd_, fqdn_rev_,
@@ -865,10 +868,13 @@ public:
 
 
             hostname_ = getRawColumnValue(r, row, HOSTNAME_COL);
             hostname_ = getRawColumnValue(r, row, HOSTNAME_COL);
 
 
+            /// @todo: implement this in #3557.
+            HWAddrPtr hwaddr;
+
             Lease6Ptr result(new Lease6(lease_type_, addr, duid_ptr, iaid_,
             Lease6Ptr result(new Lease6(lease_type_, addr, duid_ptr, iaid_,
                                         pref_lifetime_, valid_lifetime_, 0, 0,
                                         pref_lifetime_, valid_lifetime_, 0, 0,
                                         subnet_id_, fqdn_fwd_, fqdn_rev_,
                                         subnet_id_, fqdn_fwd_, fqdn_rev_,
-                                        hostname_, prefix_len_));
+                                        hostname_, hwaddr, prefix_len_));
             result->cltt_ = cltt_;
             result->cltt_ = cltt_;
             return (result);
             return (result);
         } catch (const std::exception& ex) {
         } catch (const std::exception& ex) {

+ 38 - 28
src/lib/dhcpsrv/tests/alloc_engine_unittest.cc

@@ -246,7 +246,7 @@ public:
         Lease6Ptr lease;
         Lease6Ptr lease;
         EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
         EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
                         duid_, iaid_, hint, type, false, false,
                         duid_, iaid_, hint, type, false, false,
-                        "", fake, CalloutHandlePtr(), old_leases_)));
+                        "", fake, CalloutHandlePtr(), old_leases_, HWAddrPtr())));
 
 
         // Check that we got a lease
         // Check that we got a lease
         EXPECT_TRUE(lease);
         EXPECT_TRUE(lease);
@@ -310,7 +310,7 @@ public:
         Lease6Ptr lease;
         Lease6Ptr lease;
         EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
         EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
                         duid_, iaid_, requested, type, false, false, "", false,
                         duid_, iaid_, requested, type, false, false, "", false,
-                        CalloutHandlePtr(), old_leases_)));
+                        CalloutHandlePtr(), old_leases_, HWAddrPtr())));
 
 
         // Check that we got a lease
         // Check that we got a lease
         ASSERT_TRUE(lease);
         ASSERT_TRUE(lease);
@@ -354,7 +354,8 @@ public:
         Lease6Ptr lease;
         Lease6Ptr lease;
         EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
         EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
                         duid_, iaid_, hint, type, false,
                         duid_, iaid_, hint, type, false,
-                        false, "", false, CalloutHandlePtr(), old_leases_)));
+                        false, "", false, CalloutHandlePtr(), old_leases_,
+                        HWAddrPtr())));
 
 
         // Check that we got a lease
         // Check that we got a lease
         ASSERT_TRUE(lease);
         ASSERT_TRUE(lease);
@@ -449,7 +450,7 @@ public:
         if (lease->client_id_ && clientid_) {
         if (lease->client_id_ && clientid_) {
             EXPECT_TRUE(*lease->client_id_ == *clientid_);
             EXPECT_TRUE(*lease->client_id_ == *clientid_);
         }
         }
-        EXPECT_TRUE(lease->hwaddr_ == hwaddr_->hwaddr_);
+        EXPECT_TRUE(*lease->hwaddr_ == *hwaddr_);
         /// @todo: check cltt
         /// @todo: check cltt
      }
      }
 
 
@@ -563,13 +564,15 @@ TEST_F(AllocEngine6Test, allocateAddress6Nulls) {
     Lease6Ptr lease;
     Lease6Ptr lease;
     EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(
     EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(
                     Subnet6Ptr(), duid_, iaid_, IOAddress("::"), Lease::TYPE_NA,
                     Subnet6Ptr(), duid_, iaid_, IOAddress("::"), Lease::TYPE_NA,
-                    false, false, "", false, CalloutHandlePtr(), old_leases_)));
+                    false, false, "", false, CalloutHandlePtr(), old_leases_,
+                    HWAddrPtr())));
     ASSERT_FALSE(lease);
     ASSERT_FALSE(lease);
 
 
     // Allocations without DUID are not allowed either
     // Allocations without DUID are not allowed either
     EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
     EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
                     DuidPtr(), iaid_, IOAddress("::"), Lease::TYPE_NA, false,
                     DuidPtr(), iaid_, IOAddress("::"), Lease::TYPE_NA, false,
-                    false, "", false, CalloutHandlePtr(), old_leases_)));
+                    false, "", false, CalloutHandlePtr(), old_leases_,
+                    HWAddrPtr())));
     ASSERT_FALSE(lease);
     ASSERT_FALSE(lease);
 }
 }
 
 
@@ -818,7 +821,7 @@ TEST_F(AllocEngine6Test, smallPool6) {
     EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
     EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
                     duid_, iaid_, IOAddress("::"), Lease::TYPE_NA, fqdn_fwd_,
                     duid_, iaid_, IOAddress("::"), Lease::TYPE_NA, fqdn_fwd_,
                     fqdn_rev_, hostname_, false, CalloutHandlePtr(),
                     fqdn_rev_, hostname_, false, CalloutHandlePtr(),
-                    old_leases_)));
+                    old_leases_, HWAddrPtr())));
 
 
     // Check that we got that single lease
     // Check that we got that single lease
     ASSERT_TRUE(lease);
     ASSERT_TRUE(lease);
@@ -863,7 +866,8 @@ TEST_F(AllocEngine6Test, outOfAddresses6) {
     DuidPtr other_duid = DuidPtr(new DUID(vector<uint8_t>(12, 0xff)));
     DuidPtr other_duid = DuidPtr(new DUID(vector<uint8_t>(12, 0xff)));
     const uint32_t other_iaid = 3568;
     const uint32_t other_iaid = 3568;
     Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, other_duid, other_iaid,
     Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, other_duid, other_iaid,
-                               501, 502, 503, 504, subnet_->getID(), 0));
+                               501, 502, 503, 504, subnet_->getID(),
+                               HWAddrPtr(), 0));
     lease->cltt_ = time(NULL) - 10; // Allocated 10 seconds ago
     lease->cltt_ = time(NULL) - 10; // Allocated 10 seconds ago
     ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
     ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
 
 
@@ -872,7 +876,7 @@ TEST_F(AllocEngine6Test, outOfAddresses6) {
     Lease6Ptr lease2;
     Lease6Ptr lease2;
     EXPECT_NO_THROW(lease2 = expectOneLease(engine->allocateLeases6(subnet_,
     EXPECT_NO_THROW(lease2 = expectOneLease(engine->allocateLeases6(subnet_,
                     duid_, iaid_, IOAddress("::"), Lease::TYPE_NA, false, false,
                     duid_, iaid_, IOAddress("::"), Lease::TYPE_NA, false, false,
-                    "", false, CalloutHandlePtr(), old_leases_)));
+                    "", false, CalloutHandlePtr(), old_leases_, HWAddrPtr())));
     EXPECT_FALSE(lease2);
     EXPECT_FALSE(lease2);
 
 
 }
 }
@@ -895,7 +899,8 @@ TEST_F(AllocEngine6Test, solicitReuseExpiredLease6) {
     DuidPtr other_duid = DuidPtr(new DUID(vector<uint8_t>(12, 0xff)));
     DuidPtr other_duid = DuidPtr(new DUID(vector<uint8_t>(12, 0xff)));
     const uint32_t other_iaid = 3568;
     const uint32_t other_iaid = 3568;
     Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, other_duid, other_iaid,
     Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, other_duid, other_iaid,
-                               501, 502, 503, 504, subnet_->getID(), 0));
+                               501, 502, 503, 504, subnet_->getID(),
+                               HWAddrPtr(), 0));
     lease->cltt_ = time(NULL) - 500; // Allocated 500 seconds ago
     lease->cltt_ = time(NULL) - 500; // Allocated 500 seconds ago
     lease->valid_lft_ = 495; // Lease was valid for 495 seconds
     lease->valid_lft_ = 495; // Lease was valid for 495 seconds
     ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
     ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
@@ -907,7 +912,7 @@ TEST_F(AllocEngine6Test, solicitReuseExpiredLease6) {
     EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
     EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
                     duid_, iaid_, IOAddress("::"), Lease::TYPE_NA, fqdn_fwd_,
                     duid_, iaid_, IOAddress("::"), Lease::TYPE_NA, fqdn_fwd_,
                     fqdn_rev_, hostname_, true, CalloutHandlePtr(),
                     fqdn_rev_, hostname_, true, CalloutHandlePtr(),
-                    old_leases_)));
+                    old_leases_, HWAddrPtr())));
     // Check that we got that single lease
     // Check that we got that single lease
     ASSERT_TRUE(lease);
     ASSERT_TRUE(lease);
     EXPECT_EQ(addr, lease->addr_);
     EXPECT_EQ(addr, lease->addr_);
@@ -918,7 +923,7 @@ TEST_F(AllocEngine6Test, solicitReuseExpiredLease6) {
     // CASE 2: Asking specifically for this address
     // CASE 2: Asking specifically for this address
     EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
     EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
                     duid_, iaid_, addr, Lease::TYPE_NA, false, false, "",
                     duid_, iaid_, addr, Lease::TYPE_NA, false, false, "",
-                    true, CalloutHandlePtr(), old_leases_)));
+                    true, CalloutHandlePtr(), old_leases_, HWAddrPtr())));
 
 
     // Check that we got that single lease
     // Check that we got that single lease
     ASSERT_TRUE(lease);
     ASSERT_TRUE(lease);
@@ -946,7 +951,8 @@ TEST_F(AllocEngine6Test, requestReuseExpiredLease6) {
     const uint32_t other_iaid = 3568;
     const uint32_t other_iaid = 3568;
     const SubnetID other_subnetid = 999;
     const SubnetID other_subnetid = 999;
     Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, other_duid, other_iaid,
     Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, other_duid, other_iaid,
-                               501, 502, 503, 504, other_subnetid, 0));
+                               501, 502, 503, 504, other_subnetid, HWAddrPtr(),
+                               0));
     lease->cltt_ = time(NULL) - 500; // Allocated 500 seconds ago
     lease->cltt_ = time(NULL) - 500; // Allocated 500 seconds ago
     lease->valid_lft_ = 495; // Lease was valid for 495 seconds
     lease->valid_lft_ = 495; // Lease was valid for 495 seconds
     lease->fqdn_fwd_ = true;
     lease->fqdn_fwd_ = true;
@@ -957,7 +963,7 @@ TEST_F(AllocEngine6Test, requestReuseExpiredLease6) {
     // A client comes along, asking specifically for this address
     // A client comes along, asking specifically for this address
     EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
     EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
                     duid_, iaid_, addr, Lease::TYPE_NA, false, false, "",
                     duid_, iaid_, addr, Lease::TYPE_NA, false, false, "",
-                    false, CalloutHandlePtr(), old_leases_)));
+                    false, CalloutHandlePtr(), old_leases_, HWAddrPtr())));
 
 
     // Check that he got that single lease
     // Check that he got that single lease
     ASSERT_TRUE(lease);
     ASSERT_TRUE(lease);
@@ -1114,10 +1120,11 @@ TEST_F(AllocEngine4Test, allocWithUsedHint4) {
     ASSERT_TRUE(engine);
     ASSERT_TRUE(engine);
 
 
     // Let's create a lease and put it in the LeaseMgr
     // Let's create a lease and put it in the LeaseMgr
-    uint8_t hwaddr2[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe};
+    uint8_t hwaddr2_data[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe};
+    HWAddrPtr hwaddr2(new HWAddr(hwaddr2_data, sizeof(hwaddr2_data), HTYPE_ETHER));
     uint8_t clientid2[] = { 8, 7, 6, 5, 4, 3, 2, 1 };
     uint8_t clientid2[] = { 8, 7, 6, 5, 4, 3, 2, 1 };
     time_t now = time(NULL);
     time_t now = time(NULL);
-    Lease4Ptr used(new Lease4(IOAddress("192.0.2.106"), hwaddr2, sizeof(hwaddr2),
+    Lease4Ptr used(new Lease4(IOAddress("192.0.2.106"), hwaddr2,
                               clientid2, sizeof(clientid2), 1, 2, 3, now, subnet_->getID()));
                               clientid2, sizeof(clientid2), 1, 2, 3, now, subnet_->getID()));
     ASSERT_TRUE(LeaseMgrFactory::instance().addLease(used));
     ASSERT_TRUE(LeaseMgrFactory::instance().addLease(used));
 
 
@@ -1371,10 +1378,11 @@ TEST_F(AllocEngine4Test, outOfAddresses4) {
     cfg_mgr.addSubnet4(subnet_);
     cfg_mgr.addSubnet4(subnet_);
 
 
     // Just a different hw/client-id for the second client
     // Just a different hw/client-id for the second client
-    uint8_t hwaddr2[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe};
+    uint8_t hwaddr2_data[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe};
+    HWAddrPtr hwaddr2(new HWAddr(hwaddr2_data, sizeof(hwaddr2_data), HTYPE_ETHER));
     uint8_t clientid2[] = { 8, 7, 6, 5, 4, 3, 2, 1 };
     uint8_t clientid2[] = { 8, 7, 6, 5, 4, 3, 2, 1 };
     time_t now = time(NULL);
     time_t now = time(NULL);
-    Lease4Ptr lease(new Lease4(addr, hwaddr2, sizeof(hwaddr2), clientid2,
+    Lease4Ptr lease(new Lease4(addr, hwaddr2, clientid2,
                                sizeof(clientid2), 501, 502, 503, now,
                                sizeof(clientid2), 501, 502, 503, now,
                                subnet_->getID()));
                                subnet_->getID()));
     lease->cltt_ = time(NULL) - 10; // Allocated 10 seconds ago
     lease->cltt_ = time(NULL) - 10; // Allocated 10 seconds ago
@@ -1410,11 +1418,11 @@ TEST_F(AllocEngine4Test, discoverReuseExpiredLease4) {
     cfg_mgr.addSubnet4(subnet_);
     cfg_mgr.addSubnet4(subnet_);
 
 
     // Just a different hw/client-id for the second client
     // Just a different hw/client-id for the second client
-    uint8_t hwaddr2[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe};
+    uint8_t hwaddr2_data[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe};
+    HWAddrPtr hwaddr2(new HWAddr(hwaddr2_data, sizeof(hwaddr2_data), HTYPE_ETHER));
     uint8_t clientid2[] = { 8, 7, 6, 5, 4, 3, 2, 1 };
     uint8_t clientid2[] = { 8, 7, 6, 5, 4, 3, 2, 1 };
     time_t now = time(NULL) - 500; // Allocated 500 seconds ago
     time_t now = time(NULL) - 500; // Allocated 500 seconds ago
-    Lease4Ptr lease(new Lease4(addr, clientid2, sizeof(clientid2),
-                               hwaddr2, sizeof(hwaddr2),
+    Lease4Ptr lease(new Lease4(addr, hwaddr2, clientid2, sizeof(clientid2),
                                495, 100, 200, now, subnet_->getID()));
                                495, 100, 200, now, subnet_->getID()));
     // Copy the lease, so as it can be compared with the old lease returned
     // Copy the lease, so as it can be compared with the old lease returned
     // by the allocation engine.
     // by the allocation engine.
@@ -1470,10 +1478,11 @@ TEST_F(AllocEngine4Test, requestReuseExpiredLease4) {
     IOAddress addr("192.0.2.105");
     IOAddress addr("192.0.2.105");
 
 
     // Just a different hw/client-id for the second client
     // Just a different hw/client-id for the second client
-    uint8_t hwaddr2[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe};
+    uint8_t hwaddr2_data[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe};
+    HWAddrPtr hwaddr2(new HWAddr(hwaddr2_data, sizeof(hwaddr2_data), HTYPE_ETHER));
     uint8_t clientid2[] = { 8, 7, 6, 5, 4, 3, 2, 1 };
     uint8_t clientid2[] = { 8, 7, 6, 5, 4, 3, 2, 1 };
     time_t now = time(NULL) - 500; // Allocated 500 seconds ago
     time_t now = time(NULL) - 500; // Allocated 500 seconds ago
-    Lease4Ptr lease(new Lease4(addr, clientid2, sizeof(clientid2), hwaddr2,
+    Lease4Ptr lease(new Lease4(addr, hwaddr2, clientid2, sizeof(clientid2),
                                sizeof(hwaddr2), 495, 100, 200, now,
                                sizeof(hwaddr2), 495, 100, 200, now,
                                subnet_->getID()));
                                subnet_->getID()));
     // Make a copy of the lease, so as we can comapre that with the old lease
     // Make a copy of the lease, so as we can comapre that with the old lease
@@ -1528,10 +1537,11 @@ TEST_F(AllocEngine4Test, renewLease4) {
     const time_t old_timestamp = time(NULL) - 45; // Allocated 45 seconds ago
     const time_t old_timestamp = time(NULL) - 45; // Allocated 45 seconds ago
 
 
     // Just a different hw/client-id for the second client
     // Just a different hw/client-id for the second client
-    const uint8_t hwaddr2[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe};
+    const uint8_t hwaddr2_data[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe};
+    HWAddrPtr hwaddr2(new HWAddr(hwaddr2_data, sizeof(hwaddr2_data), HTYPE_ETHER));
     const uint8_t clientid2[] = { 8, 7, 6, 5, 4, 3, 2, 1 };
     const uint8_t clientid2[] = { 8, 7, 6, 5, 4, 3, 2, 1 };
-    Lease4Ptr lease(new Lease4(addr, clientid2, sizeof(clientid2), hwaddr2,
-                               sizeof(hwaddr2), old_lifetime, old_t1, old_t2,
+    Lease4Ptr lease(new Lease4(addr, hwaddr2, clientid2, sizeof(clientid2),
+                               old_lifetime, old_t1, old_t2,
                                old_timestamp, subnet_->getID()));
                                old_timestamp, subnet_->getID()));
     ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
     ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
 
 
@@ -1685,7 +1695,7 @@ TEST_F(HookAllocEngine6Test, lease6_select) {
     Lease6Ptr lease;
     Lease6Ptr lease;
     EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
     EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
                     duid_, iaid_, IOAddress("::"), Lease::TYPE_NA, false, false,
                     duid_, iaid_, IOAddress("::"), Lease::TYPE_NA, false, false,
-                    "", false, callout_handle, old_leases_)));
+                    "", false, callout_handle, old_leases_, HWAddrPtr())));
     // Check that we got a lease
     // Check that we got a lease
     ASSERT_TRUE(lease);
     ASSERT_TRUE(lease);
 
 
@@ -1756,7 +1766,7 @@ TEST_F(HookAllocEngine6Test, change_lease6_select) {
     Lease6Ptr lease;
     Lease6Ptr lease;
     EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
     EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(subnet_,
                     duid_, iaid_, IOAddress("::"), Lease::TYPE_NA, false, false,
                     duid_, iaid_, IOAddress("::"), Lease::TYPE_NA, false, false,
-                    "", false, callout_handle, old_leases_)));
+                    "", false, callout_handle, old_leases_, HWAddrPtr())));
     // Check that we got a lease
     // Check that we got a lease
     ASSERT_TRUE(lease);
     ASSERT_TRUE(lease);
 
 

+ 12 - 4
src/lib/dhcpsrv/tests/csv_lease_file4_unittest.cc

@@ -63,10 +63,18 @@ public:
     /// @brief Object providing access to lease file IO.
     /// @brief Object providing access to lease file IO.
     LeaseFileIO io_;
     LeaseFileIO io_;
 
 
+    /// @brief hardware address 0 (corresponds to HWADDR0 const)
+    HWAddrPtr hwaddr0_;
+
+    /// @brief hardware address 1 (corresponds to HWADDR1 const)
+    HWAddrPtr hwaddr1_;
+
 };
 };
 
 
 CSVLeaseFile4Test::CSVLeaseFile4Test()
 CSVLeaseFile4Test::CSVLeaseFile4Test()
     : filename_(absolutePath("leases4.csv")), io_(filename_) {
     : filename_(absolutePath("leases4.csv")), io_(filename_) {
+    hwaddr0_.reset(new HWAddr(HWADDR0, sizeof(HWADDR0), HTYPE_ETHER));
+    hwaddr1_.reset(new HWAddr(HWADDR1, sizeof(HWADDR1), HTYPE_ETHER));
 }
 }
 
 
 std::string
 std::string
@@ -103,7 +111,7 @@ TEST_F(CSVLeaseFile4Test, parse) {
 
 
     // Verify that the lease attributes are correct.
     // Verify that the lease attributes are correct.
     EXPECT_EQ("192.0.2.1", lease->addr_.toText());
     EXPECT_EQ("192.0.2.1", lease->addr_.toText());
-    HWAddr hwaddr1(lease->hwaddr_, HTYPE_ETHER);
+    HWAddr hwaddr1(*lease->hwaddr_);
     EXPECT_EQ("06:07:08:09:0a:bc", hwaddr1.toText(false));
     EXPECT_EQ("06:07:08:09:0a:bc", hwaddr1.toText(false));
     EXPECT_FALSE(lease->client_id_);
     EXPECT_FALSE(lease->client_id_);
     EXPECT_EQ(200, lease->valid_lft_);
     EXPECT_EQ(200, lease->valid_lft_);
@@ -122,7 +130,7 @@ TEST_F(CSVLeaseFile4Test, parse) {
     ASSERT_TRUE(lease);
     ASSERT_TRUE(lease);
     // Verify that the third lease is correct.
     // Verify that the third lease is correct.
     EXPECT_EQ("192.0.3.15", lease->addr_.toText());
     EXPECT_EQ("192.0.3.15", lease->addr_.toText());
-    HWAddr hwaddr3(lease->hwaddr_, HTYPE_ETHER);
+    HWAddr hwaddr3(*lease->hwaddr_);
     EXPECT_EQ("dd:de:ba:0d:1b:2e:3e:4f", hwaddr3.toText(false));
     EXPECT_EQ("dd:de:ba:0d:1b:2e:3e:4f", hwaddr3.toText(false));
     ASSERT_TRUE(lease->client_id_);
     ASSERT_TRUE(lease->client_id_);
     EXPECT_EQ("0a:00:01:04", lease->client_id_->toText());
     EXPECT_EQ("0a:00:01:04", lease->client_id_->toText());
@@ -151,14 +159,14 @@ TEST_F(CSVLeaseFile4Test, recreate) {
     ASSERT_TRUE(io_.exists());
     ASSERT_TRUE(io_.exists());
     // Create first lease, with NULL client id.
     // Create first lease, with NULL client id.
     Lease4Ptr lease(new Lease4(IOAddress("192.0.3.2"),
     Lease4Ptr lease(new Lease4(IOAddress("192.0.3.2"),
-                               HWADDR0, sizeof(HWADDR0),
+                               hwaddr0_,
                                NULL, 0,
                                NULL, 0,
                                200, 50, 80, 0, 8, true, true,
                                200, 50, 80, 0, 8, true, true,
                                "host.example.com"));
                                "host.example.com"));
     ASSERT_NO_THROW(lf->append(*lease));
     ASSERT_NO_THROW(lf->append(*lease));
     // Create second lease, with non-NULL client id.
     // Create second lease, with non-NULL client id.
     lease.reset(new Lease4(IOAddress("192.0.3.10"),
     lease.reset(new Lease4(IOAddress("192.0.3.10"),
-                           HWADDR1, sizeof(HWADDR1),
+                           hwaddr1_,
                            CLIENTID0, sizeof(CLIENTID0),
                            CLIENTID0, sizeof(CLIENTID0),
                            100, 60, 90, 0, 7));
                            100, 60, 90, 0, 7));
     ASSERT_NO_THROW(lf->append(*lease));
     ASSERT_NO_THROW(lf->append(*lease));

+ 8 - 8
src/lib/dhcpsrv/tests/csv_lease_file6_unittest.cc

@@ -86,14 +86,14 @@ void
 CSVLeaseFile6Test::writeSampleFile() const {
 CSVLeaseFile6Test::writeSampleFile() const {
     io_.writeFile("address,duid,valid_lifetime,expire,subnet_id,"
     io_.writeFile("address,duid,valid_lifetime,expire,subnet_id,"
                   "pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,"
                   "pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,"
-                  "fqdn_rev,hostname\n"
+                  "fqdn_rev,hostname,hwaddr\n"
                   "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,"
                   "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,"
                   "200,200,8,100,0,7,0,1,1,host.example.com\n"
                   "200,200,8,100,0,7,0,1,1,host.example.com\n"
                   "2001:db8:1::1,,200,200,8,100,0,7,0,1,1,host.example.com\n"
                   "2001:db8:1::1,,200,200,8,100,0,7,0,1,1,host.example.com\n"
                   "2001:db8:2::10,01:01:01:01:0a:01:02:03:04:05,300,300,6,150,"
                   "2001:db8:2::10,01:01:01:01:0a:01:02:03:04:05,300,300,6,150,"
                   "0,8,0,0,0,\n"
                   "0,8,0,0,0,\n"
                   "3000:1::,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,0,200,8,0,2,"
                   "3000:1::,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,0,200,8,0,2,"
-                  "16,64,0,0,\n");
+                  "16,64,0,0,,\n");
 }
 }
 
 
 // This test checks the capability to read and parse leases from the file.
 // This test checks the capability to read and parse leases from the file.
@@ -192,25 +192,25 @@ TEST_F(CSVLeaseFile6Test, recreate) {
     lease.reset(new Lease6(Lease::TYPE_NA, IOAddress("2001:db8:2::10"),
     lease.reset(new Lease6(Lease::TYPE_NA, IOAddress("2001:db8:2::10"),
                            makeDUID(DUID1, sizeof(DUID1)),
                            makeDUID(DUID1, sizeof(DUID1)),
                            8, 150, 300, 40, 70, 6, false, false,
                            8, 150, 300, 40, 70, 6, false, false,
-                           "", 128));
+                           "", HWAddrPtr(), 128));
     lease->cltt_ = 0;
     lease->cltt_ = 0;
     ASSERT_NO_THROW(lf->append(*lease));
     ASSERT_NO_THROW(lf->append(*lease));
 
 
     lease.reset(new Lease6(Lease::TYPE_PD, IOAddress("3000:1:1::"),
     lease.reset(new Lease6(Lease::TYPE_PD, IOAddress("3000:1:1::"),
                            makeDUID(DUID0, sizeof(DUID0)),
                            makeDUID(DUID0, sizeof(DUID0)),
                            7, 150, 300, 40, 70, 10, false, false,
                            7, 150, 300, 40, 70, 10, false, false,
-                           "", 64));
+                           "", HWAddrPtr(), 64));
     lease->cltt_ = 0;
     lease->cltt_ = 0;
     ASSERT_NO_THROW(lf->append(*lease));
     ASSERT_NO_THROW(lf->append(*lease));
 
 
     EXPECT_EQ("address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,"
     EXPECT_EQ("address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,"
-              "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname\n"
+              "lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr\n"
               "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,"
               "2001:db8:1::1,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,"
-              "200,200,8,100,0,7,0,1,1,host.example.com\n"
+              "200,200,8,100,0,7,0,1,1,host.example.com,\n"
               "2001:db8:2::10,01:01:01:01:0a:01:02:03:04:05"
               "2001:db8:2::10,01:01:01:01:0a:01:02:03:04:05"
-              ",300,300,6,150,0,8,128,0,0,\n"
+              ",300,300,6,150,0,8,128,0,0,,\n"
               "3000:1:1::,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,"
               "3000:1:1::,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,"
-              "300,300,10,150,2,7,64,0,0,\n",
+              "300,300,10,150,2,7,64,0,0,,\n",
               io_.readFile());
               io_.readFile());
 }
 }
 
 

+ 79 - 35
src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc

@@ -86,7 +86,7 @@ GenericLeaseMgrTest::initializeLease4(std::string address) {
 
 
     // Set other parameters.  For historical reasons, address 0 is not used.
     // Set other parameters.  For historical reasons, address 0 is not used.
     if (address == straddress4_[0]) {
     if (address == straddress4_[0]) {
-        lease->hwaddr_ = vector<uint8_t>(6, 0x08);
+        lease->hwaddr_.reset(new HWAddr(vector<uint8_t>(6, 0x08), HTYPE_ETHER));
         lease->client_id_ = ClientIdPtr(new ClientId(vector<uint8_t>(8, 0x42)));
         lease->client_id_ = ClientIdPtr(new ClientId(vector<uint8_t>(8, 0x42)));
         lease->valid_lft_ = 8677;
         lease->valid_lft_ = 8677;
         lease->cltt_ = 168256;
         lease->cltt_ = 168256;
@@ -96,7 +96,7 @@ GenericLeaseMgrTest::initializeLease4(std::string address) {
         lease->hostname_ = "myhost.example.com.";
         lease->hostname_ = "myhost.example.com.";
 
 
         } else if (address == straddress4_[1]) {
         } else if (address == straddress4_[1]) {
-        lease->hwaddr_ = vector<uint8_t>(6, 0x19);
+        lease->hwaddr_.reset(new HWAddr(vector<uint8_t>(6, 0x19), HTYPE_ETHER));
         lease->client_id_ = ClientIdPtr(
         lease->client_id_ = ClientIdPtr(
             new ClientId(vector<uint8_t>(8, 0x53)));
             new ClientId(vector<uint8_t>(8, 0x53)));
         lease->valid_lft_ = 3677;
         lease->valid_lft_ = 3677;
@@ -107,7 +107,7 @@ GenericLeaseMgrTest::initializeLease4(std::string address) {
         lease->hostname_ = "myhost.example.com.";
         lease->hostname_ = "myhost.example.com.";
 
 
     } else if (address == straddress4_[2]) {
     } else if (address == straddress4_[2]) {
-        lease->hwaddr_ = vector<uint8_t>(6, 0x2a);
+        lease->hwaddr_.reset(new HWAddr(vector<uint8_t>(6, 0x2a), HTYPE_ETHER));
         lease->client_id_ = ClientIdPtr(new ClientId(vector<uint8_t>(8, 0x64)));
         lease->client_id_ = ClientIdPtr(new ClientId(vector<uint8_t>(8, 0x64)));
         lease->valid_lft_ = 5412;
         lease->valid_lft_ = 5412;
         lease->cltt_ = 234567;
         lease->cltt_ = 234567;
@@ -117,7 +117,8 @@ GenericLeaseMgrTest::initializeLease4(std::string address) {
         lease->hostname_ = "";
         lease->hostname_ = "";
 
 
     } else if (address == straddress4_[3]) {
     } else if (address == straddress4_[3]) {
-        lease->hwaddr_ = vector<uint8_t>(6, 0x19);      // Same as lease 1
+        // Hardware address same as lease 1.
+        lease->hwaddr_.reset(new HWAddr(vector<uint8_t>(6, 0x19), HTYPE_ETHER));
         lease->client_id_ = ClientIdPtr(
         lease->client_id_ = ClientIdPtr(
             new ClientId(vector<uint8_t>(8, 0x75)));
             new ClientId(vector<uint8_t>(8, 0x75)));
 
 
@@ -133,7 +134,7 @@ GenericLeaseMgrTest::initializeLease4(std::string address) {
         lease->hostname_ = "otherhost.example.com.";
         lease->hostname_ = "otherhost.example.com.";
 
 
     } else if (address == straddress4_[4]) {
     } else if (address == straddress4_[4]) {
-        lease->hwaddr_ = vector<uint8_t>(6, 0x4c);
+        lease->hwaddr_.reset(new HWAddr(vector<uint8_t>(6, 0x4c), HTYPE_ETHER));
         // Same ClientId as straddr4_[1]
         // Same ClientId as straddr4_[1]
         lease->client_id_ = ClientIdPtr(
         lease->client_id_ = ClientIdPtr(
             new ClientId(vector<uint8_t>(8, 0x53)));    // Same as lease 1
             new ClientId(vector<uint8_t>(8, 0x53)));    // Same as lease 1
@@ -145,7 +146,8 @@ GenericLeaseMgrTest::initializeLease4(std::string address) {
         lease->hostname_ = "otherhost.example.com.";
         lease->hostname_ = "otherhost.example.com.";
 
 
     } else if (address == straddress4_[5]) {
     } else if (address == straddress4_[5]) {
-        lease->hwaddr_ = vector<uint8_t>(6, 0x19);      // Same as lease 1
+        // Same as lease 1
+        lease->hwaddr_.reset(new HWAddr(vector<uint8_t>(6, 0x19), HTYPE_ETHER));
         // Same ClientId and IAID as straddress4_1
         // Same ClientId and IAID as straddress4_1
         lease->client_id_ = ClientIdPtr(
         lease->client_id_ = ClientIdPtr(
             new ClientId(vector<uint8_t>(8, 0x53)));    // Same as lease 1
             new ClientId(vector<uint8_t>(8, 0x53)));    // Same as lease 1
@@ -156,7 +158,7 @@ GenericLeaseMgrTest::initializeLease4(std::string address) {
         lease->fqdn_fwd_ = false;
         lease->fqdn_fwd_ = false;
         lease->hostname_ = "otherhost.example.com.";
         lease->hostname_ = "otherhost.example.com.";
     } else if (address == straddress4_[6]) {
     } else if (address == straddress4_[6]) {
-        lease->hwaddr_ = vector<uint8_t>(6, 0x6e);
+        lease->hwaddr_.reset(new HWAddr(vector<uint8_t>(6, 0x6e), HTYPE_ETHER));
         // Same ClientId as straddress4_1
         // Same ClientId as straddress4_1
         lease->client_id_ = ClientIdPtr(
         lease->client_id_ = ClientIdPtr(
             new ClientId(vector<uint8_t>(8, 0x53)));    // Same as lease 1
             new ClientId(vector<uint8_t>(8, 0x53)));    // Same as lease 1
@@ -168,7 +170,7 @@ GenericLeaseMgrTest::initializeLease4(std::string address) {
         lease->hostname_ = "myhost.example.com.";
         lease->hostname_ = "myhost.example.com.";
 
 
     } else if (address == straddress4_[7]) {
     } else if (address == straddress4_[7]) {
-        lease->hwaddr_ = vector<uint8_t>();             // Empty
+        lease->hwaddr_.reset(new HWAddr(vector<uint8_t>(), HTYPE_ETHER)); // Empty
         lease->client_id_ = ClientIdPtr();              // Empty
         lease->client_id_ = ClientIdPtr();              // Empty
         lease->valid_lft_ = 7975;
         lease->valid_lft_ = 7975;
         lease->cltt_ = 213876;
         lease->cltt_ = 213876;
@@ -467,7 +469,7 @@ GenericLeaseMgrTest::testLease4NullClientId() {
     EXPECT_FALSE(lmptr_->addLease(leases[1]));
     EXPECT_FALSE(lmptr_->addLease(leases[1]));
 
 
     // Check that we can get the lease by HWAddr
     // Check that we can get the lease by HWAddr
-    HWAddr tmp(leases[2]->hwaddr_, HTYPE_ETHER);
+    HWAddr tmp(*leases[2]->hwaddr_);
     Lease4Collection returned = lmptr_->getLease4(tmp);
     Lease4Collection returned = lmptr_->getLease4(tmp);
     ASSERT_EQ(1, returned.size());
     ASSERT_EQ(1, returned.size());
     detailCompareLease(leases[2], *returned.begin());
     detailCompareLease(leases[2], *returned.begin());
@@ -504,7 +506,7 @@ void
 GenericLeaseMgrTest::testGetLease4HWAddr1() {
 GenericLeaseMgrTest::testGetLease4HWAddr1() {
     // Let's initialize two different leases 4 and just add the first ...
     // Let's initialize two different leases 4 and just add the first ...
     Lease4Ptr leaseA = initializeLease4(straddress4_[5]);
     Lease4Ptr leaseA = initializeLease4(straddress4_[5]);
-    HWAddr hwaddrA(leaseA->hwaddr_, HTYPE_ETHER);
+    HWAddr hwaddrA(*leaseA->hwaddr_);
     HWAddr hwaddrB(vector<uint8_t>(6, 0x80), HTYPE_ETHER);
     HWAddr hwaddrB(vector<uint8_t>(6, 0x80), HTYPE_ETHER);
 
 
     EXPECT_TRUE(lmptr_->addLease(leaseA));
     EXPECT_TRUE(lmptr_->addLease(leaseA));
@@ -528,7 +530,7 @@ GenericLeaseMgrTest::testGetLease4HWAddr2() {
 
 
     // Get the leases matching the hardware address of lease 1
     // Get the leases matching the hardware address of lease 1
     /// @todo: Simply use HWAddr directly once 2589 is implemented
     /// @todo: Simply use HWAddr directly once 2589 is implemented
-    HWAddr tmp(leases[1]->hwaddr_, HTYPE_ETHER);
+    HWAddr tmp(*leases[1]->hwaddr_);
     Lease4Collection returned = lmptr_->getLease4(tmp);
     Lease4Collection returned = lmptr_->getLease4(tmp);
 
 
     // Should be three leases, matching leases[1], [3] and [5].
     // Should be three leases, matching leases[1], [3] and [5].
@@ -546,15 +548,13 @@ GenericLeaseMgrTest::testGetLease4HWAddr2() {
     EXPECT_EQ(straddress4_[5], addresses[2]);
     EXPECT_EQ(straddress4_[5], addresses[2]);
 
 
     // Repeat test with just one expected match
     // Repeat test with just one expected match
-    /// @todo: Simply use HWAddr directly once 2589 is implemented
-    returned = lmptr_->getLease4(HWAddr(leases[2]->hwaddr_, HTYPE_ETHER));
+    returned = lmptr_->getLease4(*leases[2]->hwaddr_);
     ASSERT_EQ(1, returned.size());
     ASSERT_EQ(1, returned.size());
     detailCompareLease(leases[2], *returned.begin());
     detailCompareLease(leases[2], *returned.begin());
 
 
     // Check that an empty vector is valid
     // Check that an empty vector is valid
-    EXPECT_TRUE(leases[7]->hwaddr_.empty());
-    /// @todo: Simply use HWAddr directly once 2589 is implemented
-    returned = lmptr_->getLease4(HWAddr(leases[7]->hwaddr_, HTYPE_ETHER));
+    EXPECT_TRUE(leases[7]->hwaddr_->hwaddr_.empty());
+    returned = lmptr_->getLease4(*leases[7]->hwaddr_);
     ASSERT_EQ(1, returned.size());
     ASSERT_EQ(1, returned.size());
     detailCompareLease(leases[7], *returned.begin());
     detailCompareLease(leases[7], *returned.begin());
 
 
@@ -573,9 +573,9 @@ GenericLeaseMgrTest::testGetLease4ClientIdHWAddrSubnetId() {
     // a lease may coexist with other leases with non NULL client id.
     // a lease may coexist with other leases with non NULL client id.
     leaseC->client_id_.reset();
     leaseC->client_id_.reset();
 
 
-    HWAddr hwaddrA(leaseA->hwaddr_, HTYPE_ETHER);
-    HWAddr hwaddrB(leaseB->hwaddr_, HTYPE_ETHER);
-    HWAddr hwaddrC(leaseC->hwaddr_, HTYPE_ETHER);
+    HWAddr hwaddrA(*leaseA->hwaddr_);
+    HWAddr hwaddrB(*leaseB->hwaddr_);
+    HWAddr hwaddrC(*leaseC->hwaddr_);
     EXPECT_TRUE(lmptr_->addLease(leaseA));
     EXPECT_TRUE(lmptr_->addLease(leaseA));
     EXPECT_TRUE(lmptr_->addLease(leaseB));
     EXPECT_TRUE(lmptr_->addLease(leaseB));
     EXPECT_TRUE(lmptr_->addLease(leaseC));
     EXPECT_TRUE(lmptr_->addLease(leaseC));
@@ -874,6 +874,48 @@ GenericLeaseMgrTest::testMaxDate6() {
     detailCompareLease(leases[1], l_returned);
     detailCompareLease(leases[1], l_returned);
 }
 }
 
 
+// Checks whether a MAC address can be stored and retrieved together with
+// a lease.
+void
+GenericLeaseMgrTest::testLease6MAC() {
+    // Get the leases to be used for the test.
+    vector<Lease6Ptr> leases = createLeases6();
+
+    HWAddrPtr hwaddr1(new HWAddr(vector<uint8_t>(6, 11), HTYPE_ETHER));
+    HWAddrPtr hwaddr2(new HWAddr(vector<uint8_t>(6, 22), HTYPE_ETHER));
+
+    leases[1]->hwaddr_ = hwaddr1;     // Add hardware address to leases 1 and 2
+    leases[2]->hwaddr_ = hwaddr2;
+    leases[3]->hwaddr_ = HWAddrPtr(); // No hardware address for the third one
+
+    // Start the tests.  Add three leases to the database, read them back and
+    // check they are what we think they are.
+    EXPECT_TRUE(lmptr_->addLease(leases[1]));
+    EXPECT_TRUE(lmptr_->addLease(leases[2]));
+    EXPECT_TRUE(lmptr_->addLease(leases[3]));
+    lmptr_->commit();
+
+    // Reopen the database to ensure that they actually got stored.
+    reopen(V6);
+
+    // First lease should have a hardware address in it
+    Lease6Ptr stored1 = lmptr_->getLease6(leasetype6_[1], ioaddress6_[1]);
+    ASSERT_TRUE(stored1);
+    ASSERT_TRUE(stored1->hwaddr_);
+    EXPECT_TRUE(*hwaddr1 == *stored1->hwaddr_);
+
+    // Second lease should have a hardware address in it
+    Lease6Ptr stored2 = lmptr_->getLease6(leasetype6_[2], ioaddress6_[2]);
+    ASSERT_TRUE(stored2);
+    ASSERT_TRUE(stored2->hwaddr_);
+    EXPECT_TRUE(*hwaddr2 == *stored2->hwaddr_);
+
+    // Third lease should NOT have any hardware address.
+    Lease6Ptr stored3 = lmptr_->getLease6(leasetype6_[3], ioaddress6_[3]);
+    ASSERT_TRUE(stored3);
+    EXPECT_FALSE(stored3->hwaddr_);
+}
+
 void
 void
 GenericLeaseMgrTest::testLease4InvalidHostname() {
 GenericLeaseMgrTest::testLease4InvalidHostname() {
     // Get the leases to be used for the test.
     // Get the leases to be used for the test.
@@ -926,11 +968,11 @@ GenericLeaseMgrTest::testGetLease4HWAddrSize() {
 
 
     // Now add leases with increasing hardware address size.
     // Now add leases with increasing hardware address size.
     for (uint8_t i = 0; i <= HWAddr::MAX_HWADDR_LEN; ++i) {
     for (uint8_t i = 0; i <= HWAddr::MAX_HWADDR_LEN; ++i) {
-        leases[1]->hwaddr_.resize(i, i);
+        leases[1]->hwaddr_->hwaddr_.resize(i, i);
         EXPECT_TRUE(lmptr_->addLease(leases[1]));
         EXPECT_TRUE(lmptr_->addLease(leases[1]));
         /// @todo: Simply use HWAddr directly once 2589 is implemented
         /// @todo: Simply use HWAddr directly once 2589 is implemented
         Lease4Collection returned =
         Lease4Collection returned =
-            lmptr_->getLease4(HWAddr(leases[1]->hwaddr_, HTYPE_ETHER));
+            lmptr_->getLease4(*leases[1]->hwaddr_);
 
 
         ASSERT_EQ(1, returned.size());
         ASSERT_EQ(1, returned.size());
         detailCompareLease(leases[1], *returned.begin());
         detailCompareLease(leases[1], *returned.begin());
@@ -939,8 +981,7 @@ GenericLeaseMgrTest::testGetLease4HWAddrSize() {
 
 
     // Database should not let us add one that is too big
     // Database should not let us add one that is too big
     // (The 42 is a random value put in each byte of the address.)
     // (The 42 is a random value put in each byte of the address.)
-    /// @todo: 2589 will make this test impossible
-    leases[1]->hwaddr_.resize(HWAddr::MAX_HWADDR_LEN + 100, 42);
+    leases[1]->hwaddr_->hwaddr_.resize(HWAddr::MAX_HWADDR_LEN + 100, 42);
     EXPECT_THROW(lmptr_->addLease(leases[1]), isc::dhcp::DbOperationError);
     EXPECT_THROW(lmptr_->addLease(leases[1]), isc::dhcp::DbOperationError);
 }
 }
 
 
@@ -955,8 +996,8 @@ GenericLeaseMgrTest::testGetLease4HWAddrSubnetId() {
     // Get the leases matching the hardware address of lease 1 and
     // Get the leases matching the hardware address of lease 1 and
     // subnet ID of lease 1.  Result should be a single lease - lease 1.
     // subnet ID of lease 1.  Result should be a single lease - lease 1.
     /// @todo: Simply use HWAddr directly once 2589 is implemented
     /// @todo: Simply use HWAddr directly once 2589 is implemented
-    Lease4Ptr returned = lmptr_->getLease4(HWAddr(leases[1]->hwaddr_,
-        HTYPE_ETHER), leases[1]->subnet_id_);
+    Lease4Ptr returned = lmptr_->getLease4(*leases[1]->hwaddr_,
+                                           leases[1]->subnet_id_);
 
 
     ASSERT_TRUE(returned);
     ASSERT_TRUE(returned);
     detailCompareLease(leases[1], returned);
     detailCompareLease(leases[1], returned);
@@ -964,8 +1005,7 @@ GenericLeaseMgrTest::testGetLease4HWAddrSubnetId() {
     // Try for a match to the hardware address of lease 1 and the wrong
     // Try for a match to the hardware address of lease 1 and the wrong
     // subnet ID.
     // subnet ID.
     /// @todo: Simply use HWAddr directly once 2589 is implemented
     /// @todo: Simply use HWAddr directly once 2589 is implemented
-    returned = lmptr_->getLease4(HWAddr(leases[1]->hwaddr_, HTYPE_ETHER),
-                                 leases[1]->subnet_id_ + 1);
+    returned = lmptr_->getLease4(*leases[1]->hwaddr_, leases[1]->subnet_id_ + 1);
     EXPECT_FALSE(returned);
     EXPECT_FALSE(returned);
 
 
     // Try for a match to the subnet ID of lease 1 (and lease 4) but
     // Try for a match to the subnet ID of lease 1 (and lease 4) but
@@ -992,9 +1032,8 @@ GenericLeaseMgrTest::testGetLease4HWAddrSubnetId() {
     leases[1]->addr_ = leases[2]->addr_;
     leases[1]->addr_ = leases[2]->addr_;
     EXPECT_TRUE(lmptr_->addLease(leases[1]));
     EXPECT_TRUE(lmptr_->addLease(leases[1]));
     /// @todo: Simply use HWAddr directly once 2589 is implemented
     /// @todo: Simply use HWAddr directly once 2589 is implemented
-    EXPECT_THROW(returned = lmptr_->getLease4(HWAddr(leases[1]->hwaddr_,
-                                                    HTYPE_ETHER),
-                                             leases[1]->subnet_id_),
+    EXPECT_THROW(returned = lmptr_->getLease4(*leases[1]->hwaddr_,
+                                              leases[1]->subnet_id_),
                  isc::dhcp::MultipleRecords);
                  isc::dhcp::MultipleRecords);
 
 
 
 
@@ -1008,11 +1047,10 @@ GenericLeaseMgrTest::testGetLease4HWAddrSubnetIdSize() {
     // Now add leases with increasing hardware address size and check
     // Now add leases with increasing hardware address size and check
     // that they can be retrieved.
     // that they can be retrieved.
     for (uint8_t i = 0; i <= HWAddr::MAX_HWADDR_LEN; ++i) {
     for (uint8_t i = 0; i <= HWAddr::MAX_HWADDR_LEN; ++i) {
-        leases[1]->hwaddr_.resize(i, i);
+        leases[1]->hwaddr_->hwaddr_.resize(i, i);
         EXPECT_TRUE(lmptr_->addLease(leases[1]));
         EXPECT_TRUE(lmptr_->addLease(leases[1]));
         /// @todo: Simply use HWAddr directly once 2589 is implemented
         /// @todo: Simply use HWAddr directly once 2589 is implemented
-        Lease4Ptr returned = lmptr_->getLease4(HWAddr(leases[1]->hwaddr_,
-                                                      HTYPE_ETHER),
+        Lease4Ptr returned = lmptr_->getLease4(*leases[1]->hwaddr_,
                                                leases[1]->subnet_id_);
                                                leases[1]->subnet_id_);
         ASSERT_TRUE(returned);
         ASSERT_TRUE(returned);
         detailCompareLease(leases[1], returned);
         detailCompareLease(leases[1], returned);
@@ -1021,7 +1059,7 @@ GenericLeaseMgrTest::testGetLease4HWAddrSubnetIdSize() {
 
 
     // Database should not let us add one that is too big
     // Database should not let us add one that is too big
     // (The 42 is a random value put in each byte of the address.)
     // (The 42 is a random value put in each byte of the address.)
-    leases[1]->hwaddr_.resize(HWAddr::MAX_HWADDR_LEN + 100, 42);
+    leases[1]->hwaddr_->hwaddr_.resize(HWAddr::MAX_HWADDR_LEN + 100, 42);
     EXPECT_THROW(lmptr_->addLease(leases[1]), isc::dhcp::DbOperationError);
     EXPECT_THROW(lmptr_->addLease(leases[1]), isc::dhcp::DbOperationError);
 }
 }
 
 
@@ -1058,7 +1096,7 @@ GenericLeaseMgrTest::testGetLease4ClientId2() {
 
 
     // Check that client-id is NULL
     // Check that client-id is NULL
     EXPECT_FALSE(leases[7]->client_id_);
     EXPECT_FALSE(leases[7]->client_id_);
-    HWAddr tmp(leases[7]->hwaddr_, HTYPE_ETHER);
+    HWAddr tmp(*leases[7]->hwaddr_);
     returned = lmptr_->getLease4(tmp);
     returned = lmptr_->getLease4(tmp);
     ASSERT_EQ(1, returned.size());
     ASSERT_EQ(1, returned.size());
     detailCompareLease(leases[7], *returned.begin());
     detailCompareLease(leases[7], *returned.begin());
@@ -1564,6 +1602,12 @@ GenericLeaseMgrTest::testNullDuid() {
     ASSERT_THROW(lmptr_->addLease(leases[1]), DbOperationError);
     ASSERT_THROW(lmptr_->addLease(leases[1]), DbOperationError);
 }
 }
 
 
+void
+GenericLeaseMgrTest::testVersion(int major, int minor) {
+    EXPECT_EQ(major, lmptr_->getVersion().first);
+    EXPECT_EQ(minor, lmptr_->getVersion().second);
+}
+
 
 
 }; // namespace test
 }; // namespace test
 }; // namespace dhcp
 }; // namespace dhcp

+ 8 - 1
src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.h

@@ -183,9 +183,11 @@ public:
     /// IPv6 address) works.
     /// IPv6 address) works.
     void testBasicLease6();
     void testBasicLease6();
 
 
-    /// @brief checks that invalid dates are safely handled.
+    /// @brief Checks that invalid dates are safely handled.
     void testMaxDate6();
     void testMaxDate6();
 
 
+    /// @brief Checks that Lease6 can be stored with and without a hardware address.
+    void testLease6MAC();
 
 
     /// @brief Test that IPv6 lease can be added, retrieved and deleted.
     /// @brief Test that IPv6 lease can be added, retrieved and deleted.
     ///
     ///
@@ -255,6 +257,11 @@ public:
     /// @brief Verifies that a null DUID is not allowed.
     /// @brief Verifies that a null DUID is not allowed.
     void testNullDuid();
     void testNullDuid();
 
 
+    /// @brief Verifies that the backend reports expected version numbers.
+    /// @param major Expected major version to be reported.
+    /// @param minor Expected minor version to be reported.
+    void testVersion(int major, int minor);
+
     /// @brief String forms of IPv4 addresses
     /// @brief String forms of IPv4 addresses
     std::vector<std::string>  straddress4_;
     std::vector<std::string>  straddress4_;
 
 

+ 166 - 25
src/lib/dhcpsrv/tests/lease_unittest.cc

@@ -18,6 +18,7 @@
 #include <dhcpsrv/lease.h>
 #include <dhcpsrv/lease.h>
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
 #include <vector>
 #include <vector>
+#include <sstream>
 
 
 using namespace isc;
 using namespace isc;
 using namespace isc::asiolink;
 using namespace isc::asiolink;
@@ -52,14 +53,26 @@ Lease4 createLease4(const std::string& hostname, const bool fqdn_fwd,
     return (lease);
     return (lease);
 }
 }
 
 
+/// @brief Fixture class used in Lease4 testing.
+class Lease4Test : public ::testing::Test {
+public:
+
+    /// Default constructor
+    ///
+    /// Currently it only initializes hardware address.
+    Lease4Test() {
+        hwaddr_.reset(new HWAddr(HWADDR, sizeof(HWADDR), HTYPE_ETHER));
+    }
+
+    /// Hardware address, used by tests.
+    HWAddrPtr hwaddr_;
+};
+
 /// Lease4 is also defined in lease_mgr.h, so is tested in this file as well.
 /// Lease4 is also defined in lease_mgr.h, so is tested in this file as well.
 // This test checks if the Lease4 structure can be instantiated correctly
 // This test checks if the Lease4 structure can be instantiated correctly
-TEST(Lease4, constructor) {
+TEST_F(Lease4Test, constructor) {
 
 
     // Random values for the tests
     // Random values for the tests
-    const uint8_t HWADDR[] = {0x08, 0x00, 0x2b, 0x02, 0x3f, 0x4e};
-    std::vector<uint8_t> hwaddr(HWADDR, HWADDR + sizeof(HWADDR));
-
     const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54};
     const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54};
     std::vector<uint8_t> clientid_vec(CLIENTID, CLIENTID + sizeof(CLIENTID));
     std::vector<uint8_t> clientid_vec(CLIENTID, CLIENTID + sizeof(CLIENTID));
     ClientId clientid(clientid_vec);
     ClientId clientid(clientid_vec);
@@ -80,14 +93,14 @@ TEST(Lease4, constructor) {
     for (int i = 0; i < sizeof(ADDRESS) / sizeof(ADDRESS[0]); ++i) {
     for (int i = 0; i < sizeof(ADDRESS) / sizeof(ADDRESS[0]); ++i) {
 
 
         // Create the lease
         // Create the lease
-        Lease4 lease(ADDRESS[i], HWADDR, sizeof(HWADDR),
+        Lease4 lease(ADDRESS[i], hwaddr_,
                      CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, 0, 0,
                      CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, 0, 0,
                      current_time, SUBNET_ID, true, true,
                      current_time, SUBNET_ID, true, true,
                      "hostname.example.com.");
                      "hostname.example.com.");
 
 
         EXPECT_EQ(ADDRESS[i], static_cast<uint32_t>(lease.addr_));
         EXPECT_EQ(ADDRESS[i], static_cast<uint32_t>(lease.addr_));
         EXPECT_EQ(0, lease.ext_);
         EXPECT_EQ(0, lease.ext_);
-        EXPECT_TRUE(hwaddr == lease.hwaddr_);
+        EXPECT_TRUE(hwaddr_ == lease.hwaddr_);
         EXPECT_TRUE(clientid == *lease.client_id_);
         EXPECT_TRUE(clientid == *lease.client_id_);
         EXPECT_EQ(0, lease.t1_);
         EXPECT_EQ(0, lease.t1_);
         EXPECT_EQ(0, lease.t2_);
         EXPECT_EQ(0, lease.t2_);
@@ -103,11 +116,7 @@ TEST(Lease4, constructor) {
 }
 }
 
 
 // This test verfies that copy constructor copies Lease4 fields correctly.
 // This test verfies that copy constructor copies Lease4 fields correctly.
-TEST(Lease4, copyConstructor) {
-
-    // Random values for the tests
-    const uint8_t HWADDR[] = {0x08, 0x00, 0x2b, 0x02, 0x3f, 0x4e};
-    std::vector<uint8_t> hwaddr(HWADDR, HWADDR + sizeof(HWADDR));
+TEST_F(Lease4Test, copyConstructor) {
 
 
     const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54};
     const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54};
     std::vector<uint8_t> clientid_vec(CLIENTID, CLIENTID + sizeof(CLIENTID));
     std::vector<uint8_t> clientid_vec(CLIENTID, CLIENTID + sizeof(CLIENTID));
@@ -121,7 +130,7 @@ TEST(Lease4, copyConstructor) {
     const uint32_t VALID_LIFETIME = 500;
     const uint32_t VALID_LIFETIME = 500;
 
 
     // Create the lease
     // Create the lease
-    Lease4 lease(0xffffffff, HWADDR, sizeof(HWADDR),
+    Lease4 lease(0xffffffff, hwaddr_,
                  CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, 0, 0, current_time,
                  CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, 0, 0, current_time,
                  SUBNET_ID);
                  SUBNET_ID);
 
 
@@ -133,16 +142,25 @@ TEST(Lease4, copyConstructor) {
     EXPECT_TRUE(lease == copied_lease);
     EXPECT_TRUE(lease == copied_lease);
     // Client IDs are equal, but they should be in two distinct pointers.
     // Client IDs are equal, but they should be in two distinct pointers.
     EXPECT_FALSE(lease.client_id_ == copied_lease.client_id_);
     EXPECT_FALSE(lease.client_id_ == copied_lease.client_id_);
+
+    // Hardware addresses are equal, but they should point to two objects,
+    // each holding the same data. The content should be equal...
+    EXPECT_TRUE(*lease.hwaddr_ == *copied_lease.hwaddr_);
+
+    // ... but it should point to different objects.
+    EXPECT_FALSE(lease.hwaddr_ == copied_lease.hwaddr_);
+
+    // Now let's check that the hwaddr pointer is copied even if it's NULL:
+    lease.hwaddr_.reset();
+    Lease4 copied_lease2(lease);
+    EXPECT_TRUE(lease == copied_lease2);
 }
 }
 
 
 // This test verfies that the assignment operator copies all Lease4 fields
 // This test verfies that the assignment operator copies all Lease4 fields
 // correctly.
 // correctly.
-TEST(Lease4, operatorAssign) {
+TEST_F(Lease4Test, operatorAssign) {
 
 
     // Random values for the tests
     // Random values for the tests
-    const uint8_t HWADDR[] = {0x08, 0x00, 0x2b, 0x02, 0x3f, 0x4e};
-    std::vector<uint8_t> hwaddr(HWADDR, HWADDR + sizeof(HWADDR));
-
     const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54};
     const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54};
     std::vector<uint8_t> clientid_vec(CLIENTID, CLIENTID + sizeof(CLIENTID));
     std::vector<uint8_t> clientid_vec(CLIENTID, CLIENTID + sizeof(CLIENTID));
     ClientId clientid(clientid_vec);
     ClientId clientid(clientid_vec);
@@ -155,7 +173,7 @@ TEST(Lease4, operatorAssign) {
     const uint32_t VALID_LIFETIME = 500;
     const uint32_t VALID_LIFETIME = 500;
 
 
     // Create the lease
     // Create the lease
-    Lease4 lease(0xffffffff, HWADDR, sizeof(HWADDR),
+    Lease4 lease(0xffffffff, hwaddr_,
                  CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, 0, 0, current_time,
                  CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, 0, 0, current_time,
                  SUBNET_ID);
                  SUBNET_ID);
 
 
@@ -167,21 +185,39 @@ TEST(Lease4, operatorAssign) {
     EXPECT_TRUE(lease == copied_lease);
     EXPECT_TRUE(lease == copied_lease);
     // Client IDs are equal, but they should be in two distinct pointers.
     // Client IDs are equal, but they should be in two distinct pointers.
     EXPECT_FALSE(lease.client_id_ == copied_lease.client_id_);
     EXPECT_FALSE(lease.client_id_ == copied_lease.client_id_);
+
+    // Hardware addresses are equal, but they should point to two objects,
+    // each holding the same data. The content should be equal...
+    EXPECT_TRUE(*lease.hwaddr_ == *copied_lease.hwaddr_);
+
+    // ... but it should point to different objects.
+    EXPECT_FALSE(lease.hwaddr_ == copied_lease.hwaddr_);
+
+    // Now let's check that the hwaddr pointer is copied even if it's NULL:
+    lease.hwaddr_.reset();
+    copied_lease = lease;
+    EXPECT_TRUE(lease == copied_lease);
 }
 }
 
 
 // This test verifies that the matches() returns true if two leases differ
 // This test verifies that the matches() returns true if two leases differ
 // by values other than address, HW address, Client ID and ext_.
 // by values other than address, HW address, Client ID and ext_.
-TEST(Lease4, matches) {
+TEST_F(Lease4Test, matches) {
     // Create two leases which share the same address, HW address, client id
     // Create two leases which share the same address, HW address, client id
     // and ext_ value.
     // and ext_ value.
     const time_t current_time = time(NULL);
     const time_t current_time = time(NULL);
-    Lease4 lease1(IOAddress("192.0.2.3"), HWADDR, sizeof(HWADDR), CLIENTID,
+    Lease4 lease1(IOAddress("192.0.2.3"), hwaddr_, CLIENTID,
                   sizeof(CLIENTID), VALID_LIFETIME, current_time, 0, 0,
                   sizeof(CLIENTID), VALID_LIFETIME, current_time, 0, 0,
                   SUBNET_ID);
                   SUBNET_ID);
     lease1.hostname_ = "lease1.example.com.";
     lease1.hostname_ = "lease1.example.com.";
     lease1.fqdn_fwd_ = true;
     lease1.fqdn_fwd_ = true;
     lease1.fqdn_rev_ = true;
     lease1.fqdn_rev_ = true;
-    Lease4 lease2(IOAddress("192.0.2.3"), HWADDR, sizeof(HWADDR), CLIENTID,
+
+    // We need to make an explicit copy. Otherwise the second lease will just
+    // store a pointer and we'll have two leases pointing to a single HWAddr.
+    // That would make modifications to only one impossible.
+    HWAddrPtr hwcopy(new HWAddr(*hwaddr_));
+
+    Lease4 lease2(IOAddress("192.0.2.3"), hwcopy, CLIENTID,
                   sizeof(CLIENTID), VALID_LIFETIME + 10, current_time - 10,
                   sizeof(CLIENTID), VALID_LIFETIME + 10, current_time - 10,
                   100, 200, SUBNET_ID);
                   100, 200, SUBNET_ID);
     lease2.hostname_ = "lease2.example.com.";
     lease2.hostname_ = "lease2.example.com.";
@@ -198,7 +234,7 @@ TEST(Lease4, matches) {
     lease1.addr_ = lease2.addr_;
     lease1.addr_ = lease2.addr_;
 
 
     // Change HW address, leases should not match.
     // Change HW address, leases should not match.
-    lease1.hwaddr_[1] += 1;
+    lease1.hwaddr_->hwaddr_[1] += 1;
     EXPECT_FALSE(lease1.matches(lease2));
     EXPECT_FALSE(lease1.matches(lease2));
     lease1.hwaddr_ = lease2.hwaddr_;
     lease1.hwaddr_ = lease2.hwaddr_;
 
 
@@ -220,7 +256,7 @@ TEST(Lease4, matches) {
 /// Checks that the operator==() correctly compares two leases for equality.
 /// Checks that the operator==() correctly compares two leases for equality.
 /// As operator!=() is also defined for this class, every check on operator==()
 /// As operator!=() is also defined for this class, every check on operator==()
 /// is followed by the reverse check on operator!=().
 /// is followed by the reverse check on operator!=().
-TEST(Lease4, operatorEquals) {
+TEST_F(Lease4Test, operatorEquals) {
 
 
     // Random values for the tests
     // Random values for the tests
     const uint32_t ADDRESS = 0x01020304;
     const uint32_t ADDRESS = 0x01020304;
@@ -234,10 +270,16 @@ TEST(Lease4, operatorEquals) {
     const uint32_t VALID_LIFETIME = 500;
     const uint32_t VALID_LIFETIME = 500;
 
 
     // Check when the leases are equal.
     // Check when the leases are equal.
-    Lease4 lease1(ADDRESS, HWADDR, sizeof(HWADDR),
+    Lease4 lease1(ADDRESS, hwaddr_,
                   CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, current_time, 0,
                   CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, current_time, 0,
                   0, SUBNET_ID);
                   0, SUBNET_ID);
-    Lease4 lease2(ADDRESS, HWADDR, sizeof(HWADDR),
+
+    // We need to make an explicit copy. Otherwise the second lease will just
+    // store a pointer and we'll have two leases pointing to a single HWAddr.
+    // That would make modifications to only one impossible.
+    HWAddrPtr hwcopy(new HWAddr(*hwaddr_));
+
+    Lease4 lease2(ADDRESS, hwcopy,
                   CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, current_time, 0, 0,
                   CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, current_time, 0, 0,
                   SUBNET_ID);
                   SUBNET_ID);
     EXPECT_TRUE(lease1 == lease2);
     EXPECT_TRUE(lease1 == lease2);
@@ -259,7 +301,7 @@ TEST(Lease4, operatorEquals) {
     EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
     EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
     EXPECT_FALSE(lease1 != lease2); // ... leases equal
     EXPECT_FALSE(lease1 != lease2); // ... leases equal
 
 
-    ++lease1.hwaddr_[0];
+    ++lease1.hwaddr_->hwaddr_[0];
     EXPECT_FALSE(lease1 == lease2);
     EXPECT_FALSE(lease1 == lease2);
     EXPECT_TRUE(lease1 != lease2);
     EXPECT_TRUE(lease1 != lease2);
     lease1.hwaddr_ = lease2.hwaddr_;
     lease1.hwaddr_ = lease2.hwaddr_;
@@ -383,6 +425,37 @@ TEST(Lease4, hasIdenticalFqdn) {
                                                      false, false)));
                                                      false, false)));
 }
 }
 
 
+// Verify that toText() method reports Lease4 structure properly.
+TEST_F(Lease4Test, toText) {
+
+    const time_t current_time = 12345678;
+    Lease4 lease(IOAddress("192.0.2.3"), hwaddr_, CLIENTID, sizeof(CLIENTID),
+                 3600, 123, 456, current_time, 789);
+    
+    std::stringstream expected;
+    expected << "Address:       192.0.2.3\n"
+             << "Valid life:    3600\n"
+             << "T1:            123\n"
+             << "T2:            456\n"
+             << "Cltt:          12345678\n"
+             << "Hardware addr: " << hwaddr_->toText(false) << "\n"
+             << "Subnet ID:     789\n";
+
+    EXPECT_EQ(expected.str(), lease.toText());
+
+    // Now let's try with a lease without hardware address.
+    lease.hwaddr_.reset();
+    expected.str("");
+    expected << "Address:       192.0.2.3\n"
+             << "Valid life:    3600\n"
+             << "T1:            123\n"
+             << "T2:            456\n"
+             << "Cltt:          12345678\n"
+             << "Hardware addr: (none)\n"
+             << "Subnet ID:     789\n";
+    EXPECT_EQ(expected.str(), lease.toText());
+}
+
 /// @brief Creates an instance of the lease with certain FQDN data.
 /// @brief Creates an instance of the lease with certain FQDN data.
 ///
 ///
 /// @param hostname Hostname.
 /// @param hostname Hostname.
@@ -544,6 +617,34 @@ TEST(Lease6, matches) {
     lease1.duid_ = duid;
     lease1.duid_ = duid;
     EXPECT_FALSE(lease1.matches(lease2));
     EXPECT_FALSE(lease1.matches(lease2));
     lease1.duid_ = lease2.duid_;
     lease1.duid_ = lease2.duid_;
+
+    // Hardware address checks
+    EXPECT_TRUE(lease1.matches(lease2)); // Neither lease have hardware address.
+
+    // Let's add a hardware lease to the first one.
+    HWAddrPtr hwaddr(new HWAddr(HWADDR, sizeof(HWADDR), HTYPE_ETHER));
+    lease1.hwaddr_ = hwaddr;
+
+    // Only the first one has a hardware address, so not equal.
+    EXPECT_FALSE(lease1.matches(lease2));
+
+    // Only the second one has it, so still not equal.
+    lease1.hwaddr_.reset();
+    lease2.hwaddr_ = hwaddr;
+    EXPECT_FALSE(lease1.matches(lease2));
+
+    // Ok, now both have it - they should be equal.
+    lease1.hwaddr_ = hwaddr;
+    EXPECT_TRUE(lease1.matches(lease2));
+
+    // Let's create a second instance that have the same values.
+    HWAddrPtr hwaddr2(new HWAddr(HWADDR, sizeof(HWADDR), HTYPE_ETHER));
+    lease2.hwaddr_ = hwaddr2;
+    EXPECT_TRUE(lease1.matches(lease2));
+
+    // Let's modify the second address and check that they won't be equal anymore.
+    hwaddr2->hwaddr_[0]++;
+    EXPECT_FALSE(lease1.matches(lease2));
 }
 }
 
 
 /// @brief Lease6 Equality Test
 /// @brief Lease6 Equality Test
@@ -750,4 +851,44 @@ TEST(Lease6, hasIdenticalFqdn) {
                                                      false, false)));
                                                      false, false)));
 }
 }
 
 
+// Verify that toText() method reports Lease4 structure properly.
+TEST(Lease6, toText) {
+
+    HWAddrPtr hwaddr(new HWAddr(HWADDR, sizeof(HWADDR), HTYPE_ETHER));
+
+    uint8_t llt[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
+    DuidPtr duid(new DUID(llt, sizeof(llt)));
+    
+    Lease6 lease(Lease::TYPE_NA, IOAddress("2001:db8::1"), duid, 123456,
+                 400, 800, 100, 200, 5678, hwaddr, 128);
+    lease.cltt_ = 12345678;
+    
+    std::stringstream expected;
+    expected << "Type:          IA_NA(" << static_cast<int>(Lease::TYPE_NA) << ")\n"
+             << "Address:       2001:db8::1\n"
+             << "Prefix length: 128\n"
+             << "IAID:          123456\n"
+             << "Pref life:     400\n"
+             << "Valid life:    800\n"
+             << "Cltt:          12345678\n"
+             << "Hardware addr: " << hwaddr->toText(false) << "\n"
+             << "Subnet ID:     5678\n";
+
+    EXPECT_EQ(expected.str(), lease.toText());
+
+    // Now let's try with a lease without hardware address.
+    lease.hwaddr_.reset();
+    expected.str("");
+    expected << "Type:          IA_NA(" << static_cast<int>(Lease::TYPE_NA) << ")\n"
+             << "Address:       2001:db8::1\n"
+             << "Prefix length: 128\n"
+             << "IAID:          123456\n"
+             << "Pref life:     400\n"
+             << "Valid life:    800\n"
+             << "Cltt:          12345678\n"
+             << "Hardware addr: (none)\n"
+             << "Subnet ID:     5678\n";
+    EXPECT_EQ(expected.str(), lease.toText());
+}
+
 }; // end of anonymous namespace
 }; // end of anonymous namespace

+ 60 - 0
src/lib/dhcpsrv/tests/memfile_lease_mgr_unittest.cc

@@ -26,6 +26,7 @@
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
 
 
 #include <iostream>
 #include <iostream>
+#include <fstream>
 #include <sstream>
 #include <sstream>
 
 
 using namespace std;
 using namespace std;
@@ -421,4 +422,63 @@ TEST_F(MemfileLeaseMgrTest, DISABLED_nullDuid) {
     ASSERT_THROW(lmptr_->addLease(leases[1]), DbOperationError);
     ASSERT_THROW(lmptr_->addLease(leases[1]), DbOperationError);
 }
 }
 
 
+/// @brief Tests whether memfile can store and retrieve hardware addresses
+TEST_F(MemfileLeaseMgrTest, testLease6Mac) {
+    startBackend(V6);
+    testLease6MAC();
+}
+
+/// @brief Tests whether memfile is able to work with old CSV file (without mac)
+///
+/// Ticket #3555 introduced MAC address support in Lease6. Instead of developing
+/// an upgrade script, the code is written in a way that allows reading old CSV
+/// (i.e. format that was used in Kea 0.9), hence no upgrade is necessary.
+TEST_F(MemfileLeaseMgrTest, testUpgrade0_9_0_to_0_9_1) {
+
+    // Let's write a CSV file without hwaddr column. Sorry about the long
+    // lines, but nobody was around to whine about 80 columns limit when CSV
+    // format was invented :).
+    string csv_nohwaddr =
+        "address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname\n"
+        "2001:db8::1,42:42:42:42:42:42:42:42,3677,127133,73,3600,1,42,0,0,1,myhost.example.com.\n"
+        "2001:db8::2,3a:3a:3a:3a:3a:3a:3a:3a,5412,239979,73,1800,2,89,7,0,0,myhost.example.com.\n"
+        "2001:db8::3,1f:20:21:22:23:24:25:26,7000,241567,37,7200,0,4294967294,28,1,0,myhost.example.com.\n";
+
+    ofstream csv(getLeaseFilePath("leasefile6_0.csv").c_str(), ios::out | ios::trunc);
+    ASSERT_TRUE(csv.is_open());
+    csv << csv_nohwaddr;
+    csv.close();
+
+    startBackend(V6);
+
+    // None of the leases should have any hardware addresses assigned.
+    Lease6Ptr stored1 = lmptr_->getLease6(leasetype6_[1], ioaddress6_[1]);
+    ASSERT_TRUE(stored1);
+    EXPECT_FALSE(stored1->hwaddr_);
+
+    Lease6Ptr stored2 = lmptr_->getLease6(leasetype6_[2], ioaddress6_[2]);
+    ASSERT_TRUE(stored2);
+    EXPECT_FALSE(stored2->hwaddr_);
+
+    Lease6Ptr stored3 = lmptr_->getLease6(leasetype6_[3], ioaddress6_[3]);
+    ASSERT_TRUE(stored3);
+    EXPECT_FALSE(stored3->hwaddr_);
+}
+
+// Check that memfile reports version correctly.
+TEST_F(MemfileLeaseMgrTest, versionCheck) {
+
+    // Check that V4 backend reports versions correctly.
+    startBackend(V4);
+    testVersion(Memfile_LeaseMgr::MAJOR_VERSION,
+                Memfile_LeaseMgr::MINOR_VERSION);
+    LeaseMgrFactory::destroy();
+
+    // Check that V6 backends reports them ok, too.
+    startBackend(V6);
+    testVersion(Memfile_LeaseMgr::MAJOR_VERSION,
+                Memfile_LeaseMgr::MINOR_VERSION);
+    LeaseMgrFactory::destroy();
+}
+
 }; // end of anonymous namespace
 }; // end of anonymous namespace

+ 4 - 1
src/lib/dhcpsrv/tests/test_utils.cc

@@ -31,7 +31,10 @@ detailCompareLease(const Lease4Ptr& first, const Lease4Ptr& second) {
     // call the operator uint32_t() function, which causes an exception to be
     // call the operator uint32_t() function, which causes an exception to be
     // thrown for IPv6 addresses.
     // thrown for IPv6 addresses.
     EXPECT_EQ(first->addr_, second->addr_);
     EXPECT_EQ(first->addr_, second->addr_);
-    EXPECT_TRUE(first->hwaddr_ == second->hwaddr_);
+
+    // We need to compare the actual HWAddr objects, not pointers
+    EXPECT_TRUE(*first->hwaddr_ == *second->hwaddr_);
+
     if (first->client_id_ && second->client_id_) {
     if (first->client_id_ && second->client_id_) {
         EXPECT_TRUE(*first->client_id_ == *second->client_id_);
         EXPECT_TRUE(*first->client_id_ == *second->client_id_);
     } else {
     } else {

+ 1 - 0
src/lib/util/csv_file.cc

@@ -288,6 +288,7 @@ CSVFile::open() {
 
 
             // Check the header against the columns specified for the CSV file.
             // Check the header against the columns specified for the CSV file.
             if (!validateHeader(header)) {
             if (!validateHeader(header)) {
+
                 isc_throw(CSVFileError, "invalid header '" << header
                 isc_throw(CSVFileError, "invalid header '" << header
                           << "' in CSV file '" << filename_ << "'");
                           << "' in CSV file '" << filename_ << "'");
             }
             }

+ 21 - 3
src/lib/util/csv_file.h

@@ -178,6 +178,20 @@ public:
         writeAt(at, value.c_str());
         writeAt(at, value.c_str());
     }
     }
 
 
+    /// @brief Appends the value as a new column.
+    ///
+    /// @param value Value to be written.
+    /// @tparam T Type of the value being written.
+    template<typename T>
+    void append(const T value) {
+        try {
+            values_.push_back(boost::lexical_cast<std::string>(value));
+        } catch (const boost::bad_lexical_cast& ex) {
+            isc_throw(CSVFileError, "unable to stringify the value to be "
+                      "appended to the CSV file row.");
+        }
+    }
+
     /// @brief Replaces the value at specified index.
     /// @brief Replaces the value at specified index.
     ///
     ///
     /// This function is used to set values to be rendered using
     /// This function is used to set values to be rendered using
@@ -430,7 +444,7 @@ protected:
     /// @return true if the column is valid; false otherwise.
     /// @return true if the column is valid; false otherwise.
     virtual bool validate(const CSVRow& row);
     virtual bool validate(const CSVRow& row);
 
 
-private:
+protected:
 
 
     /// @brief This function validates the header of the CSV file.
     /// @brief This function validates the header of the CSV file.
     ///
     ///
@@ -438,12 +452,16 @@ private:
     /// compare that they exactly match (including order) the header read
     /// compare that they exactly match (including order) the header read
     /// from the file.
     /// from the file.
     ///
     ///
-    /// This function is called internally by @CSVFile::open.
+    /// This function is called internally by @CSVFile::open. Derived classes
+    /// may add extra validation steps.
+    ///
+    /// @todo There should be a support for optional columns (see ticket #3626).
     ///
     ///
     /// @param header A row holding a header.
     /// @param header A row holding a header.
     /// @return true if header matches the columns; false otherwise.
     /// @return true if header matches the columns; false otherwise.
-    bool validateHeader(const CSVRow& header);
+    virtual bool validateHeader(const CSVRow& header);
 
 
+private:
     /// @brief Sanity check if stream is open.
     /// @brief Sanity check if stream is open.
     ///
     ///
     /// Checks if the file stream is open so as IO operations can be performed
     /// Checks if the file stream is open so as IO operations can be performed

+ 19 - 0
src/lib/util/tests/csv_file_unittest.cc

@@ -90,6 +90,25 @@ TEST(CSVRow, writeAt) {
     EXPECT_THROW(row.writeAt(3, "foo"), CSVFileError);
     EXPECT_THROW(row.writeAt(3, "foo"), CSVFileError);
 }
 }
 
 
+// Checks whether writeAt() and append() can be mixed together.
+TEST(CSVRow, append) {
+    CSVRow row(3);
+
+    EXPECT_EQ(3, row.getValuesCount());
+
+    row.writeAt(0, "alpha");
+    ASSERT_NO_THROW(row.append("delta"));
+    EXPECT_EQ(4, row.getValuesCount());
+    row.writeAt(1, "beta");
+    row.writeAt(2, "gamma");
+    ASSERT_NO_THROW(row.append("epsilon"));
+    EXPECT_EQ(5, row.getValuesCount());
+
+    std::string text;
+    ASSERT_NO_THROW(text = row.render());
+    EXPECT_EQ("alpha,beta,gamma,delta,epsilon", text);
+}
+
 /// @brief Test fixture class for testing operations on CSV file.
 /// @brief Test fixture class for testing operations on CSV file.
 ///
 ///
 /// It implements basic operations on files, such as reading writing
 /// It implements basic operations on files, such as reading writing