Browse Source

[master] Merge branch 'trac5009'

Marcin Siodelski 8 years ago
parent
commit
d3109bb277

+ 1 - 1
src/bin/dhcp4/dhcp4_srv.cc

@@ -2092,7 +2092,7 @@ Dhcpv4Srv::setFixedFields(Dhcpv4Exchange& ex) {
 
 OptionPtr
 Dhcpv4Srv::getNetmaskOption(const Subnet4Ptr& subnet) {
-    uint32_t netmask = getNetmask4(subnet->get().second);
+    uint32_t netmask = getNetmask4(subnet->get().second).toUint32();
 
     OptionPtr opt(new OptionInt<uint32_t>(Option::V4,
                   DHO_SUBNET_MASK, netmask));

+ 2 - 2
src/bin/dhcp4/tests/decline_unittest.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -280,7 +280,7 @@ TEST_F(DeclineTest, declineNonMatchingIPAddress) {
 
     // Modify the client's address to force it to decline a different address
     // than it has obtained from the server.
-    client.config_.lease_.addr_ = IOAddress(static_cast<uint32_t>(leased_address) + 1);
+    client.config_.lease_.addr_ = IOAddress(leased_address.toUint32() + 1);
 
     // Send DHCPDECLINE and make sure it was unsuccessful, i.e. the lease
     // remains in the database.

+ 2 - 2
src/bin/dhcp4/tests/release_unittest.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -265,7 +265,7 @@ TEST_F(ReleaseTest, releaseNonMatchingIPAddress) {
 
     // Modify the client's address to force it to release a different address
     // than it has obtained from the server.
-    client.config_.lease_.addr_ = IOAddress(static_cast<uint32_t>(leased_address) + 1);
+    client.config_.lease_.addr_ = IOAddress(leased_address.toUint32() + 1);
 
     // Send DHCPRELEASE and make sure it was unsuccessful, i.e. the lease
     // remains in the database.

+ 3 - 3
src/bin/perfdhcp/test_control.cc

@@ -1797,7 +1797,7 @@ TestControl::sendRequest4(const TestControlSocket& socket,
     OptionPtr opt_requested_address =
         OptionPtr(new Option(Option::V4, DHO_DHCP_REQUESTED_ADDRESS,
                              OptionBuffer()));
-    opt_requested_address->setUint32(static_cast<uint32_t>(yiaddr));
+    opt_requested_address->setUint32(yiaddr.toUint32());
     pkt4->addOption(opt_requested_address);
     OptionPtr opt_parameter_list =
         Option::factory(Option::V4, DHO_DHCP_PARAMETER_REQUEST_LIST);
@@ -1916,8 +1916,8 @@ TestControl::sendRequest4(const TestControlSocket& socket,
                                              DHO_DHCP_REQUESTED_ADDRESS,
                                              OptionBuffer(),
                                              rip_offset));
-    // The IOAddress is castable to uint32_t and returns exactly what we need.
-    opt_requested_ip->setUint32(static_cast<uint32_t>(yiaddr));
+    // The IOAddress is convertible to uint32_t and returns exactly what we need.
+    opt_requested_ip->setUint32(yiaddr.toUint32());
     pkt4->addOption(opt_requested_ip);
 
     setDefaults4(socket, boost::static_pointer_cast<Pkt4>(pkt4));

+ 4 - 3
src/lib/asiolink/io_address.cc

@@ -107,7 +107,8 @@ IOAddress::isV6Multicast() const {
     return (asio_address_.to_v6().is_multicast());
 }
 
-IOAddress::operator uint32_t() const {
+uint32_t
+IOAddress::toUint32() const {
     if (asio_address_.is_v4()) {
         return (asio_address_.to_v4().to_ulong());
     } else {
@@ -128,8 +129,8 @@ IOAddress::subtract(const IOAddress& a, const IOAddress& b) {
         isc_throw(BadValue, "Both addresses have to be the same family");
     }
     if (a.isV4()) {
-        // Subtracting v4 is easy. We have uint32_t operator.
-        return (IOAddress(static_cast<uint32_t>(a) - static_cast<uint32_t>(b)));
+        // Subtracting v4 is easy. We have a conversion function to uint32_t.
+        return (IOAddress(a.toUint32() - b.toUint32()));
     } else {
         // v6 is more involved.
 

+ 2 - 2
src/lib/asiolink/io_address.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2010-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2010-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -285,7 +285,7 @@ public:
     ///
     /// \return uint32_t that represents IPv4 address in
     ///         network byte order
-    operator uint32_t () const;
+    uint32_t toUint32() const;
 
     /// @name Methods returning @c IOAddress objects encapsulating typical addresses.
     ///

+ 2 - 2
src/lib/asiolink/tests/io_address_unittest.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2011-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -172,7 +172,7 @@ TEST(IOAddressTest, uint32) {
     IOAddress addr1("192.0.2.5");
 
     // operator uint_32() is used here
-    uint32_t tmp = addr1;
+    uint32_t tmp = addr1.toUint32();
 
     uint32_t expected = (192U << 24) +  (0U << 16) + (2U << 8) + 5U;
 

+ 1 - 1
src/lib/dhcp/option4_addrlst.cc

@@ -77,7 +77,7 @@ Option4AddrLst::pack(isc::util::OutputBuffer& buf) const {
     AddressContainer::const_iterator addr = addrs_.begin();
 
     while (addr != addrs_.end()) {
-        buf.writeUint32(*addr);
+        buf.writeUint32(addr->toUint32());
         ++addr;
     }
 }

+ 4 - 4
src/lib/dhcp/pkt4.cc

@@ -106,10 +106,10 @@ Pkt4::pack() {
         buffer_out_.writeUint32(transid_);
         buffer_out_.writeUint16(secs_);
         buffer_out_.writeUint16(flags_);
-        buffer_out_.writeUint32(ciaddr_);
-        buffer_out_.writeUint32(yiaddr_);
-        buffer_out_.writeUint32(siaddr_);
-        buffer_out_.writeUint32(giaddr_);
+        buffer_out_.writeUint32(ciaddr_.toUint32());
+        buffer_out_.writeUint32(yiaddr_.toUint32());
+        buffer_out_.writeUint32(siaddr_.toUint32());
+        buffer_out_.writeUint32(giaddr_.toUint32());
 
 
         if ((hw_len > 0) && (hw_len <= MAX_CHADDR_LEN)) {

+ 2 - 2
src/lib/dhcp/pkt_filter.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -36,7 +36,7 @@ PktFilter::openFallbackSocket(const isc::asiolink::IOAddress& addr,
     struct sockaddr_in addr4;
     memset(&addr4, 0, sizeof(addr4));
     addr4.sin_family = AF_INET;
-    addr4.sin_addr.s_addr = htonl(addr);
+    addr4.sin_addr.s_addr = htonl(addr.toUint32());
     addr4.sin_port = htons(port);
 
     if (bind(sock, reinterpret_cast<struct sockaddr*>(&addr4),

+ 1 - 1
src/lib/dhcp/pkt_filter_bpf.cc

@@ -341,7 +341,7 @@ PktFilterBPF::openSocket(Iface& iface,
     // Configure the BPF program to receive unicast packets sent to the
     // specified address. The program will also allow packets sent to the
     // 255.255.255.255 broadcast address.
-    prog.bf_insns[8].k = static_cast<uint32_t>(addr);
+    prog.bf_insns[8].k = addr.toUint32();
 
     // Configure the BPF program to receive packets on the specified port.
     prog.bf_insns[11].k = port;

+ 3 - 3
src/lib/dhcp/pkt_filter_inet.cc

@@ -40,7 +40,7 @@ PktFilterInet::openSocket(Iface& iface,
     if (receive_bcast && iface.flag_broadcast_) {
         addr4.sin_addr.s_addr = INADDR_ANY;
     } else {
-        addr4.sin_addr.s_addr = htonl(addr);
+        addr4.sin_addr.s_addr = htonl(addr.toUint32());
     }
 
     int sock = socket(AF_INET, SOCK_DGRAM, 0);
@@ -218,7 +218,7 @@ PktFilterInet::send(const Iface&, uint16_t sockfd,
     memset(&to, 0, sizeof(to));
     to.sin_family = AF_INET;
     to.sin_port = htons(pkt->getRemotePort());
-    to.sin_addr.s_addr = htonl(pkt->getRemoteAddr());
+    to.sin_addr.s_addr = htonl(pkt->getRemoteAddr().toUint32());
 
     struct msghdr m;
     // Initialize our message header structure.
@@ -256,7 +256,7 @@ PktFilterInet::send(const Iface&, uint16_t sockfd,
     struct in_pktinfo* pktinfo =(struct in_pktinfo *)CMSG_DATA(cmsg);
     memset(pktinfo, 0, sizeof(struct in_pktinfo));
     pktinfo->ipi_ifindex = pkt->getIndex();
-    pktinfo->ipi_spec_dst.s_addr = htonl(pkt->getLocalAddr()); // set the source IP address
+    pktinfo->ipi_spec_dst.s_addr = htonl(pkt->getLocalAddr().toUint32()); // set the source IP address
     m.msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo));
 #endif
 

+ 1 - 1
src/lib/dhcp/pkt_filter_lpf.cc

@@ -167,7 +167,7 @@ PktFilterLPF::openSocket(Iface& iface,
     // Configure the filter program to receive unicast packets sent to the
     // specified address. The program will also allow packets sent to the
     // 255.255.255.255 broadcast address.
-    dhcp_sock_filter[8].k = static_cast<uint32_t>(addr);
+    dhcp_sock_filter[8].k = addr.toUint32();
 
     // Override the default port value.
     dhcp_sock_filter[11].k = port;

+ 3 - 3
src/lib/dhcp/protocol_util.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -168,8 +168,8 @@ writeIpUdpHeader(const Pkt4Ptr& pkt, util::OutputBuffer& out_buf) {
     out_buf.writeUint8(128); // TTL
     out_buf.writeUint8(IPPROTO_UDP); // Protocol UDP.
     out_buf.writeUint16(0); // Temporarily set checksum to 0.
-    out_buf.writeUint32(pkt->getLocalAddr()); // Source address.
-    out_buf.writeUint32(pkt->getRemoteAddr()); // Destination address.
+    out_buf.writeUint32(pkt->getLocalAddr().toUint32()); // Source address.
+    out_buf.writeUint32(pkt->getRemoteAddr().toUint32()); // Destination address.
 
     // Calculate pseudo header checksum. It will be necessary to compute
     // UDP checksum.

+ 1 - 1
src/lib/dhcp/tests/pkt_filter_test_utils.cc

@@ -105,7 +105,7 @@ PktFilterTest::sendMessage(const IOAddress& dest) {
     memset(&dest_addr4, 0, sizeof(sockaddr));
     dest_addr4.sin_family = AF_INET;
     dest_addr4.sin_port = htons(port_);
-    dest_addr4.sin_addr.s_addr = htonl(dest);
+    dest_addr4.sin_addr.s_addr = htonl(dest.toUint32());
     ASSERT_EQ(sendto(send_msg_sock_, test_message_->getBuffer().getData(),
                      test_message_->getBuffer().getLength(), 0,
                      reinterpret_cast<struct sockaddr*>(&dest_addr4),

+ 5 - 5
src/lib/dhcpsrv/addr_utilities.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -95,7 +95,7 @@ isc::asiolink::IOAddress firstAddrInPrefix4(const isc::asiolink::IOAddress& pref
     // We don't check that it is a valid IPv4 address and thus has
     // a required length of 4 bytes because it has been already
     // checked by the calling function.
-    uint32_t addr = prefix;
+    uint32_t addr = prefix.toUint32();
     return (IOAddress(addr & (~bitMask4[len])));
 }
 
@@ -112,7 +112,7 @@ isc::asiolink::IOAddress lastAddrInPrefix4(const isc::asiolink::IOAddress& prefi
         isc_throw(isc::BadValue, "Too large netmask. 0..32 is allowed in IPv4");
     }
 
-    uint32_t addr = prefix;
+    uint32_t addr = prefix.toUint32();
     return (IOAddress(addr | bitMask4[len]));
 }
 
@@ -215,8 +215,8 @@ addrsInRange(const isc::asiolink::IOAddress& min,
         // Let's explicitly cast last_ and first_ (IOAddress). This conversion is
         // automatic, but let's explicitly cast it show that we moved to integer
         // domain and addresses are now substractable.
-        uint64_t max_numeric = static_cast<uint32_t>(max);
-        uint64_t min_numeric = static_cast<uint32_t>(min);
+        uint64_t max_numeric = static_cast<uint64_t>(max.toUint32());
+        uint64_t min_numeric = static_cast<uint64_t>(min.toUint32());
 
         // We can simply subtract the values. We need to increase the result
         // by one, as both min and max are included in the range. So even if

+ 10 - 0
src/lib/dhcpsrv/base_host_data_source.h

@@ -225,6 +225,16 @@ public:
     virtual ConstHostPtr
     get6(const asiolink::IOAddress& prefix, const uint8_t prefix_len) const = 0;
 
+    /// @brief Returns a host connected to the IPv6 subnet and having
+    /// a reservation for a specified IPv6 address or prefix.
+    ///
+    /// @param subnet_id Subnet identifier.
+    /// @param address reserved IPv6 address/prefix.
+    ///
+    /// @return Const @c Host object using a specified IPv6 address/prefix.
+    virtual ConstHostPtr
+    get6(const SubnetID& subnet_id, const asiolink::IOAddress& address) const = 0;
+
     /// @brief Adds a new host to the collection.
     ///
     /// The implementations of this method should guard against duplicate

+ 4 - 4
src/lib/dhcpsrv/cql_lease_mgr.cc

@@ -563,7 +563,7 @@ public:
             // address: int
             // The address in the Lease structure is an IOAddress object.
             // Convert this to an integer for storage.
-            addr4_ = static_cast<uint32_t>(lease_->addr_);
+            addr4_ = lease_->addr_.toUint32();
             data.add(&addr4_);
 
             // hwaddr: blob
@@ -1387,7 +1387,7 @@ CqlLeaseMgr::getLease4(const isc::asiolink::IOAddress& addr) const {
     // Set up the WHERE clause value
     CqlDataArray data;
 
-    uint32_t addr4_data = static_cast<uint32_t>(addr);
+    uint32_t addr4_data = addr.toUint32();
     data.add(&addr4_data);
 
     // Get the data
@@ -1707,7 +1707,7 @@ CqlLeaseMgr::updateLease4(const Lease4Ptr& lease) {
     data.remove(0);
 
     // Set up the WHERE clause and append it to the SQL_BIND array
-    uint32_t addr4_data = static_cast<uint32_t>(lease->addr_);
+    uint32_t addr4_data = lease->addr_.toUint32();
     data.add(&addr4_data);
 
     // Drop to common update code
@@ -1805,7 +1805,7 @@ CqlLeaseMgr::deleteLease(const isc::asiolink::IOAddress& addr) {
     CqlDataArray data;
 
     if (addr.isV4()) {
-        uint32_t addr4_data = static_cast<uint32_t>(addr);
+        uint32_t addr4_data = addr.toUint32();
         data.add(&addr4_data);
         return (deleteLeaseCommon(DELETE_LEASE4, data, *exchange4_));
     } else {

+ 70 - 4
src/lib/dhcpsrv/mysql_host_data_source.cc

@@ -294,7 +294,7 @@ public:
             // ipv4_address : INT UNSIGNED NULL
             // The address in the Host structure is an IOAddress object.  Convert
             // this to an integer for storage.
-            ipv4_address_ = static_cast<uint32_t>(host->getIPv4Reservation());
+            ipv4_address_ = host->getIPv4Reservation().toUint32();
             bind_[5].buffer_type = MYSQL_TYPE_LONG;
             bind_[5].buffer = reinterpret_cast<char*>(&ipv4_address_);
             bind_[5].is_unsigned = MLM_TRUE;
@@ -327,7 +327,7 @@ public:
             // ipv4_address : INT UNSIGNED NULL
             // The address in the Host structure is an IOAddress object.  Convert
             // this to an integer for storage.
-            dhcp4_next_server_ = static_cast<uint32_t>(host->getNextServer());
+            dhcp4_next_server_ = host->getNextServer().toUint32();
             bind_[9].buffer_type = MYSQL_TYPE_LONG;
             bind_[9].buffer = reinterpret_cast<char*>(&dhcp4_next_server_);
             bind_[9].is_unsigned = MLM_TRUE;
@@ -822,6 +822,11 @@ private:
                 space.assign(space_);
             }
 
+            // If empty or null space provided, use a default top level space.
+            if (space.empty()) {
+                space = (universe_ == Option::V4 ? "dhcp4" : "dhcp6");
+            }
+
             // Convert formatted_value to string as well.
             std::string formatted_value;
             if (formatted_value_null_ == MLM_FALSE) {
@@ -1757,6 +1762,7 @@ public:
         GET_HOST_SUBID6_DHCPID, // Gets host by IPv6 SubnetID, HW address/DUID
         GET_HOST_SUBID_ADDR,    // Gets host by IPv4 SubnetID and IPv4 address
         GET_HOST_PREFIX,        // Gets host by IPv6 prefix
+        GET_HOST_SUBID6_ADDR,   // Gets host by IPv6 SubnetID and IPv6 prefix
         GET_VERSION,            // Obtain version number
         INSERT_HOST,            // Insert new host to collection
         INSERT_V6_RESRV,        // Insert v6 reservation
@@ -2046,6 +2052,30 @@ TaggedStatementArray tagged_statements = { {
                  "WHERE address = ? AND prefix_len = ?) "
             "ORDER BY h.host_id, o.option_id, r.reservation_id"},
 
+    // Retrieves host information, IPv6 reservations and DHCPv6 options
+    // associated with a host using subnet id and prefix. This query
+    // returns host information for a single host. However, multiple rows
+    // are returned due to left joining IPv6 reservations and DHCPv6 options.
+    // The number of rows returned is multiplication of number of existing
+    // IPv6 reservations and DHCPv6 options.
+    {MySqlHostDataSourceImpl::GET_HOST_SUBID6_ADDR,
+            "SELECT h.host_id, h.dhcp_identifier, "
+                "h.dhcp_identifier_type, h.dhcp4_subnet_id, "
+                "h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
+                "h.dhcp4_client_classes, h.dhcp6_client_classes, "
+                "h.dhcp4_next_server, h.dhcp4_server_hostname, h.dhcp4_boot_file_name, "
+                "o.option_id, o.code, o.value, o.formatted_value, o.space, "
+                "o.persistent, "
+                "r.reservation_id, r.address, r.prefix_len, r.type, "
+                "r.dhcp6_iaid "
+            "FROM hosts AS h "
+            "LEFT JOIN dhcp6_options AS o "
+                "ON h.host_id = o.host_id "
+            "LEFT JOIN ipv6_reservations AS r "
+                "ON h.host_id = r.host_id "
+            "WHERE h.dhcp6_subnet_id = ? AND r.address = ? "
+            "ORDER BY h.host_id, o.option_id, r.reservation_id"},
+
     // Retrieves MySQL schema version.
     {MySqlHostDataSourceImpl::GET_VERSION,
             "SELECT version, minor FROM schema_version"},
@@ -2441,7 +2471,7 @@ MySqlHostDataSource::getAll4(const asiolink::IOAddress& address) const {
     MYSQL_BIND inbind[1];
     memset(inbind, 0, sizeof(inbind));
 
-    uint32_t addr4 = static_cast<uint32_t>(address);
+    uint32_t addr4 = address.toUint32();
     inbind[0].buffer_type = MYSQL_TYPE_LONG;
     inbind[0].buffer = reinterpret_cast<char*>(&addr4);
     inbind[0].is_unsigned = MLM_TRUE;
@@ -2504,7 +2534,7 @@ MySqlHostDataSource::get4(const SubnetID& subnet_id,
     inbind[0].buffer = reinterpret_cast<char*>(&subnet);
     inbind[0].is_unsigned = MLM_TRUE;
 
-    uint32_t addr4 = static_cast<uint32_t>(address);
+    uint32_t addr4 = address.toUint32();
     inbind[1].buffer_type = MYSQL_TYPE_LONG;
     inbind[1].buffer = reinterpret_cast<char*>(&addr4);
     inbind[1].is_unsigned = MLM_TRUE;
@@ -2597,6 +2627,42 @@ MySqlHostDataSource::get6(const asiolink::IOAddress& prefix,
     return (result);
 }
 
+ConstHostPtr
+MySqlHostDataSource::get6(const SubnetID& subnet_id,
+                          const asiolink::IOAddress& address) const {
+    // Set up the WHERE clause value
+    MYSQL_BIND inbind[2];
+    memset(inbind, 0, sizeof(inbind));
+
+    uint32_t subnet_buffer = static_cast<uint32_t>(subnet_id);
+    inbind[0].buffer_type = MYSQL_TYPE_LONG;
+    inbind[0].buffer = reinterpret_cast<char*>(&subnet_buffer);
+    inbind[0].is_unsigned = MLM_TRUE;
+
+    std::string addr6 = address.toText();
+    unsigned long addr6_length = addr6.size();
+
+    inbind[1].buffer_type = MYSQL_TYPE_BLOB;
+    inbind[1].buffer = reinterpret_cast<char*>
+                        (const_cast<char*>(addr6.c_str()));
+    inbind[1].length = &addr6_length;
+    inbind[1].buffer_length = addr6_length;
+
+    ConstHostCollection collection;
+    impl_->getHostCollection(MySqlHostDataSourceImpl::GET_HOST_SUBID6_ADDR,
+                             inbind, impl_->host_ipv6_exchange_,
+                             collection, true);
+
+    // Return single record if present, else clear the host.
+    ConstHostPtr result;
+    if (!collection.empty()) {
+        result = *collection.begin();
+    }
+
+    return (result);
+}
+
+
 // Miscellaneous database methods.
 
 std::string MySqlHostDataSource::getName() const {

+ 10 - 0
src/lib/dhcpsrv/mysql_host_data_source.h

@@ -198,6 +198,16 @@ public:
     virtual ConstHostPtr
     get6(const asiolink::IOAddress& prefix, const uint8_t prefix_len) const;
 
+    /// @brief Returns a host connected to the IPv6 subnet and having
+    /// a reservation for a specified IPv6 address or prefix.
+    ///
+    /// @param subnet_id Subnet identifier.
+    /// @param address reserved IPv6 address/prefix.
+    ///
+    /// @return Const @c Host object using a specified IPv6 address/prefix.
+    virtual ConstHostPtr
+    get6(const SubnetID& subnet_id, const asiolink::IOAddress& address) const;
+
     /// @brief Adds a new host to the collection.
     ///
     /// The implementations of this method should guard against duplicate

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

@@ -361,7 +361,7 @@ public:
             // Address: uint32_t
             // The address in the Lease structure is an IOAddress object.  Convert
             // this to an integer for storage.
-            addr4_ = static_cast<uint32_t>(lease_->addr_);
+            addr4_ = lease_->addr_.toUint32();
             bind_[0].buffer_type = MYSQL_TYPE_LONG;
             bind_[0].buffer = reinterpret_cast<char*>(&addr4_);
             bind_[0].is_unsigned = MLM_TRUE;
@@ -1596,7 +1596,7 @@ MySqlLeaseMgr::getLease4(const isc::asiolink::IOAddress& addr) const {
     MYSQL_BIND inbind[1];
     memset(inbind, 0, sizeof(inbind));
 
-    uint32_t addr4 = static_cast<uint32_t>(addr);
+    uint32_t addr4 = addr.toUint32();
     inbind[0].buffer_type = MYSQL_TYPE_LONG;
     inbind[0].buffer = reinterpret_cast<char*>(&addr4);
     inbind[0].is_unsigned = MLM_TRUE;
@@ -1960,7 +1960,7 @@ MySqlLeaseMgr::updateLease4(const Lease4Ptr& lease) {
     MYSQL_BIND where;
     memset(&where, 0, sizeof(where));
 
-    uint32_t addr4 = static_cast<uint32_t>(lease->addr_);
+    uint32_t addr4 = lease->addr_.toUint32();
     where.buffer_type = MYSQL_TYPE_LONG;
     where.buffer = reinterpret_cast<char*>(&addr4);
     where.is_unsigned = MLM_TRUE;
@@ -2032,7 +2032,7 @@ MySqlLeaseMgr::deleteLease(const isc::asiolink::IOAddress& addr) {
     memset(inbind, 0, sizeof(inbind));
 
     if (addr.isV4()) {
-        uint32_t addr4 = static_cast<uint32_t>(addr);
+        uint32_t addr4 = addr.toUint32();
 
         inbind[0].buffer_type = MYSQL_TYPE_LONG;
         inbind[0].buffer = reinterpret_cast<char*>(&addr4);

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

@@ -66,7 +66,7 @@ void PsqlBindArray::add(const uint8_t& byte) {
 void PsqlBindArray::add(const isc::asiolink::IOAddress& addr) {
     if (addr.isV4()) {
         addTempString(boost::lexical_cast<std::string>
-                   (static_cast<uint32_t>(addr)));
+                   (addr.toUint32()));
     } else {
         addTempString(addr.toText());
     }

+ 103 - 18
src/lib/dhcpsrv/pgsql_host_data_source.cc

@@ -287,43 +287,62 @@ public:
             static_cast<Host::IdentifierType>(type);
 
         // dhcp4_subnet_id : INT NULL
-        uint32_t subnet_id;
-        getColumnValue(r, row, DHCP4_SUBNET_ID_COL, subnet_id);
+        uint32_t subnet_id(0);
+        if (!isColumnNull(r, row, DHCP4_SUBNET_ID_COL)) {
+            getColumnValue(r, row, DHCP4_SUBNET_ID_COL, subnet_id);
+        }
         SubnetID dhcp4_subnet_id = static_cast<SubnetID>(subnet_id);
 
         // dhcp6_subnet_id : INT NULL
-        getColumnValue(r, row, DHCP6_SUBNET_ID_COL, subnet_id);
+        subnet_id = 0;
+        if (!isColumnNull(r, row, DHCP6_SUBNET_ID_COL)) {
+            getColumnValue(r, row, DHCP6_SUBNET_ID_COL, subnet_id);
+        }
         SubnetID dhcp6_subnet_id = static_cast<SubnetID>(subnet_id);
 
         // ipv4_address : BIGINT NULL
-        uint32_t addr4;
-        getColumnValue(r, row, IPV4_ADDRESS_COL, addr4);
+        uint32_t addr4(0);
+        if (!isColumnNull(r, row, IPV4_ADDRESS_COL)) {
+            getColumnValue(r, row, IPV4_ADDRESS_COL, addr4);
+        }
         isc::asiolink::IOAddress ipv4_reservation(addr4);
 
         // hostname : VARCHAR(255) NULL
         std::string hostname;
-        getColumnValue(r, row, HOSTNAME_COL, hostname);
+        if (!isColumnNull(r, row, HOSTNAME_COL)) {
+            getColumnValue(r, row, HOSTNAME_COL, hostname);
+        }
 
         // dhcp4_client_classes : VARCHAR(255) NULL
         std::string dhcp4_client_classes;
-        getColumnValue(r, row, DHCP4_CLIENT_CLASSES_COL, dhcp4_client_classes);
+        if (!isColumnNull(r, row, DHCP4_CLIENT_CLASSES_COL)) {
+            getColumnValue(r, row, DHCP4_CLIENT_CLASSES_COL, dhcp4_client_classes);
+        }
 
         // dhcp6_client_classes : VARCHAR(255) NULL
         std::string dhcp6_client_classes;
-        getColumnValue(r, row, DHCP6_CLIENT_CLASSES_COL, dhcp6_client_classes);
+        if (!isColumnNull(r, row, DHCP6_CLIENT_CLASSES_COL)) {
+            getColumnValue(r, row, DHCP6_CLIENT_CLASSES_COL, dhcp6_client_classes);
+        }
 
         // dhcp4_next_server : BIGINT NULL
-        uint32_t dhcp4_next_server_as_uint32;
-        getColumnValue(r, row, DHCP4_NEXT_SERVER_COL, dhcp4_next_server_as_uint32);
+        uint32_t dhcp4_next_server_as_uint32(0);
+        if (!isColumnNull(r, row, DHCP4_NEXT_SERVER_COL)) {
+            getColumnValue(r, row, DHCP4_NEXT_SERVER_COL, dhcp4_next_server_as_uint32);
+        }
         isc::asiolink::IOAddress dhcp4_next_server(dhcp4_next_server_as_uint32);
 
         // dhcp4_server_hostname : VARCHAR(64)
         std::string dhcp4_server_hostname;
-        getColumnValue(r, row, DHCP4_SERVER_HOSTNAME_COL, dhcp4_server_hostname);
+        if (!isColumnNull(r, row, DHCP4_SERVER_HOSTNAME_COL)) {
+            getColumnValue(r, row, DHCP4_SERVER_HOSTNAME_COL, dhcp4_server_hostname);
+        }
 
         // dhcp4_boot_file_name : VARCHAR(128)
         std::string dhcp4_boot_file_name;
-        getColumnValue(r, row, DHCP4_BOOT_FILE_NAME_COL, dhcp4_boot_file_name);
+        if (!isColumnNull(r, row, DHCP4_BOOT_FILE_NAME_COL)) {
+            getColumnValue(r, row, DHCP4_BOOT_FILE_NAME_COL, dhcp4_boot_file_name);
+        }
 
         // Finally, attempt to create the new host.
         HostPtr host;
@@ -468,18 +487,29 @@ private:
 
             // value: BYTEA
             uint8_t value[OPTION_VALUE_MAX_LEN];
-            size_t value_len;
-            PgSqlExchange::convertFromBytea(r, row, value_index_, value,
-                                            sizeof(value), value_len);
+            size_t value_len(0);
+            if (!isColumnNull(r, row, value_index_)) {
+                PgSqlExchange::convertFromBytea(r, row, value_index_, value,
+                                                sizeof(value), value_len);
+            }
 
             // formatted_value: TEXT
             std::string formatted_value;
-            PgSqlExchange::getColumnValue(r, row, formatted_value_index_,
-                                          formatted_value);
+            if (!isColumnNull(r, row, formatted_value_index_)) {
+                PgSqlExchange::getColumnValue(r, row, formatted_value_index_,
+                                              formatted_value);
+            }
 
             // space: VARCHAR(128)
             std::string space;
-            PgSqlExchange::getColumnValue(r, row, space_index_, space);
+            if (!isColumnNull(r, row, space_index_)) {
+                PgSqlExchange::getColumnValue(r, row, space_index_, space);
+            }
+
+            // If empty or null space provided, use a default top level space.
+            if (space.empty()) {
+                space = (universe_ == Option::V4 ? "dhcp4" : "dhcp6");
+            }
 
             // persistent: BOOL default false
             bool persistent;
@@ -1148,6 +1178,7 @@ public:
         GET_HOST_SUBID6_DHCPID, // Gets host by IPv6 SubnetID, HW address/DUID
         GET_HOST_SUBID_ADDR,    // Gets host by IPv4 SubnetID and IPv4 address
         GET_HOST_PREFIX,        // Gets host by IPv6 prefix
+        GET_HOST_SUBID6_ADDR,   // Gets host by IPv6 SubnetID and IPv6 prefix
         GET_VERSION,            // Obtain version number
         INSERT_HOST,            // Insert new host to collection
         INSERT_V6_RESRV,        // Insert v6 reservation
@@ -1454,6 +1485,32 @@ TaggedStatementArray tagged_statements = { {
      "ORDER BY h.host_id, o.option_id, r.reservation_id"
     },
 
+    // PgSqlHostDataSourceImpl::GET_HOST_SUBID6_ADDR
+    // Retrieves host information, IPv6 reservations and DHCPv6 options
+    // associated with a host using IPv6 subnet id and prefix. This query
+    // returns host information for a single host. However, multiple rows
+    // are returned due to left joining IPv6 reservations and DHCPv6 options.
+    // The number of rows returned is multiplication of number of existing
+    // IPv6 reservations and DHCPv6 options.
+    {2,
+     { OID_INT4, OID_VARCHAR },
+     "get_host_subid6_addr",
+     "SELECT h.host_id, h.dhcp_identifier, "
+     "  h.dhcp_identifier_type, h.dhcp4_subnet_id, "
+     "  h.dhcp6_subnet_id, h.ipv4_address, h.hostname, "
+     "  h.dhcp4_client_classes, h.dhcp6_client_classes, "
+     "  h.dhcp4_next_server, h.dhcp4_server_hostname, h.dhcp4_boot_file_name, "
+     "  o.option_id, o.code, o.value, o.formatted_value, o.space, "
+     "  o.persistent, "
+     "  r.reservation_id, r.address, r.prefix_len, r.type, "
+     "  r.dhcp6_iaid "
+     "FROM hosts AS h "
+     "LEFT JOIN dhcp6_options AS o ON h.host_id = o.host_id "
+     "LEFT JOIN ipv6_reservations AS r ON h.host_id = r.host_id "
+     "WHERE h.dhcp6_subnet_id = $1 AND r.address = $2 "
+     "ORDER BY h.host_id, o.option_id, r.reservation_id"
+    },
+
     // PgSqlHostDataSourceImpl::GET_VERSION
     // Retrieves MySQL schema version.
     {0,
@@ -1955,6 +2012,34 @@ PgSqlHostDataSource::get6(const asiolink::IOAddress& prefix,
     return (result);
 }
 
+ConstHostPtr
+PgSqlHostDataSource::get6(const SubnetID& subnet_id,
+                          const asiolink::IOAddress& address) const {
+    /// @todo: Check that prefix is v6 address, not v4.
+
+    // Set up the WHERE clause value
+    PsqlBindArrayPtr bind_array(new PsqlBindArray());
+
+    // Add the subnet id
+    bind_array->add(subnet_id);
+
+    // Add the prefix
+    bind_array->add(address);
+
+    ConstHostCollection collection;
+    impl_->getHostCollection(PgSqlHostDataSourceImpl::GET_HOST_SUBID6_ADDR,
+                             bind_array, impl_->host_ipv6_exchange_,
+                             collection, true);
+
+    // Return single record if present, else clear the host.
+    ConstHostPtr result;
+    if (!collection.empty()) {
+        result = *collection.begin();
+    }
+
+    return (result);
+}
+
 // Miscellaneous database methods.
 
 std::string PgSqlHostDataSource::getName() const {

+ 10 - 0
src/lib/dhcpsrv/pgsql_host_data_source.h

@@ -206,6 +206,16 @@ public:
     virtual ConstHostPtr
     get6(const asiolink::IOAddress& prefix, const uint8_t prefix_len) const;
 
+    /// @brief Returns a host connected to the IPv6 subnet and having
+    /// a reservation for a specified IPv6 address or prefix.
+    ///
+    /// @param subnet_id Subnet identifier.
+    /// @param address reserved IPv6 address/prefix.
+    ///
+    /// @return Const @c Host object using a specified IPv6 address/prefix.
+    virtual ConstHostPtr
+    get6(const SubnetID& subnet_id, const asiolink::IOAddress& address) const;
+
     /// @brief Adds a new host to the collection.
     ///
     /// The method will insert the given host and all of its children (v4

+ 13 - 7
src/lib/dhcpsrv/pgsql_lease_mgr.cc

@@ -328,7 +328,7 @@ public:
 
         try {
             addr_str_ = boost::lexical_cast<std::string>
-                        (static_cast<uint32_t>(lease->addr_));
+                        (lease->addr_.toUint32());
             bind_array.add(addr_str_);
 
             if (lease->hwaddr_ && !lease->hwaddr_->hwaddr_.empty()) {
@@ -581,6 +581,15 @@ public:
     /// @throw DbOperationError if the lease cannot be created.
     Lease6Ptr convertFromDatabase(const PgSqlResult& r, int row) {
         try {
+
+            /// @todo In theory, an administrator could tweak lease
+            /// information in the database. In this case, some of the
+            /// values could be set to NULL. This is less likely than
+            /// in case of host reservations, but we may consider if
+            /// retrieved values should be checked for being NULL to
+            /// prevent cryptic errors during conversions from NULL
+            /// to actual values.
+
             isc::asiolink::IOAddress addr(getIPv6Value(r, row, ADDRESS_COL));
 
             convertFromBytea(r, row, DUID_COL, duid_buffer_,
@@ -882,9 +891,6 @@ void PgSqlLeaseMgr::getLeaseCollection(StatementIndex stindex,
                                        Exchange& exchange,
                                        LeaseCollection& result,
                                        bool single) const {
-    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
-              DHCPSRV_PGSQL_GET_ADDR4).arg(tagged_statements[stindex].name);
-
     PgSqlResult r(PQexecPrepared(conn_, tagged_statements[stindex].name,
                                  tagged_statements[stindex].nbparams,
                                  &bind_array.values_[0],
@@ -955,7 +961,7 @@ PgSqlLeaseMgr::getLease4(const isc::asiolink::IOAddress& addr) const {
 
     // LEASE ADDRESS
     std::string addr_str = boost::lexical_cast<std::string>
-                           (static_cast<uint32_t>(addr));
+                           (addr.toUint32());
     bind_array.add(addr_str);
 
     // Get the data
@@ -1241,7 +1247,7 @@ PgSqlLeaseMgr::updateLease4(const Lease4Ptr& lease) {
 
     // Set up the WHERE clause and append it to the SQL_BIND array
     std::string addr4_ = boost::lexical_cast<std::string>
-                         (static_cast<uint32_t>(lease->addr_));
+                         (lease->addr_.toUint32());
     bind_array.add(addr4_);
 
     // Drop to common update code
@@ -1292,7 +1298,7 @@ PgSqlLeaseMgr::deleteLease(const isc::asiolink::IOAddress& addr) {
 
     if (addr.isV4()) {
         std::string addr4_str = boost::lexical_cast<std::string>
-                                 (static_cast<uint32_t>(addr));
+                                 (addr.toUint32());
         bind_array.add(addr4_str);
         return (deleteLeaseCommon(DELETE_LEASE4, bind_array) > 0);
     }

+ 40 - 0
src/lib/dhcpsrv/tests/generic_host_data_source_unittest.cc

@@ -1003,6 +1003,46 @@ void GenericHostDataSourceTest::testGetByIPv6(Host::IdentifierType id,
     EXPECT_FALSE(hdsptr_->get6(IOAddress("2001:db8::5"), len));
 }
 
+void GenericHostDataSourceTest::testGetBySubnetIPv6() {
+    // Make sure we have a pointer to the host data source.
+    ASSERT_TRUE(hdsptr_);
+
+    // Let's create a couple of hosts...
+    HostPtr host1 = initializeHost6("2001:db8:1::", Host::IDENT_DUID, true);
+    HostPtr host2 = initializeHost6("2001:db8:2::", Host::IDENT_DUID, true);
+    HostPtr host3 = initializeHost6("2001:db8:3::", Host::IDENT_DUID, true);
+    HostPtr host4 = initializeHost6("2001:db8:4::", Host::IDENT_DUID, true);
+
+    // ... and add them to the data source.
+    ASSERT_NO_THROW(hdsptr_->add(host1));
+    ASSERT_NO_THROW(hdsptr_->add(host2));
+    ASSERT_NO_THROW(hdsptr_->add(host3));
+    ASSERT_NO_THROW(hdsptr_->add(host4));
+
+    // And then try to retrieve them back.
+    ConstHostPtr from_hds1 = hdsptr_->get6(host1->getIPv6SubnetID(),
+                                           IOAddress("2001:db8:1::"));
+    ConstHostPtr from_hds2 = hdsptr_->get6(host2->getIPv6SubnetID(),
+                                           IOAddress("2001:db8:2::"));
+    ConstHostPtr from_hds3 = hdsptr_->get6(host3->getIPv6SubnetID(),
+                                           IOAddress("2001:db8:3::"));
+    ConstHostPtr from_hds4 = hdsptr_->get6(host4->getIPv6SubnetID(),
+                                           IOAddress("2001:db8:4::"));
+
+    // Make sure we got something back.
+    ASSERT_TRUE(from_hds1);
+    ASSERT_TRUE(from_hds2);
+    ASSERT_TRUE(from_hds3);
+    ASSERT_TRUE(from_hds4);
+
+    // Then let's check that what we got seems correct.
+    compareHosts(host1, from_hds1);
+    compareHosts(host2, from_hds2);
+    compareHosts(host3, from_hds3);
+    compareHosts(host4, from_hds4);
+}
+
+
 void GenericHostDataSourceTest::testAddDuplicate6WithSameDUID() {
     // Make sure we have the pointer to the host data source.
     ASSERT_TRUE(hdsptr_);

+ 5 - 0
src/lib/dhcpsrv/tests/generic_host_data_source_unittest.h

@@ -418,6 +418,11 @@ public:
     /// @param prefix true - reserve IPv6 prefix, false - reserve IPv6 address
     void testGetByIPv6(Host::IdentifierType id, bool prefix);
 
+    /// @brief Test inserts several hosts with unique prefixes and checks
+    ///        that the can be retrieved by subnet id and prefix value.
+    void testGetBySubnetIPv6();
+
+
     /// @brief Test that hosts can be retrieved by hardware address.
     ///
     /// Uses gtest macros to report failures.

+ 1 - 1
src/lib/dhcpsrv/tests/lease_unittest.cc

@@ -83,7 +83,7 @@ TEST_F(Lease4Test, constructor) {
                      current_time, SUBNET_ID, true, true,
                      "hostname.example.com.");
 
-        EXPECT_EQ(ADDRESS[i], static_cast<uint32_t>(lease.addr_));
+        EXPECT_EQ(ADDRESS[i], lease.addr_.toUint32());
         EXPECT_TRUE(util::equalValues(hwaddr_, lease.hwaddr_));
         EXPECT_TRUE(util::equalValues(clientid_, lease.client_id_));
         EXPECT_EQ(0, lease.t1_);

+ 6 - 0
src/lib/dhcpsrv/tests/mysql_host_data_source_unittest.cc

@@ -335,6 +335,12 @@ TEST_F(MySqlHostDataSourceTest, get6PrefixWithHWaddr) {
     testGetByIPv6(Host::IDENT_HWADDR, true);
 }
 
+// Test verifies that host with IPv6 prefix reservation can be retrieved
+// by subnet id and prefix value.
+TEST_F(MySqlHostDataSourceTest, get6SubnetPrefix) {
+    testGetBySubnetIPv6();
+}
+
 // Test verifies if a host reservation can be added and later retrieved by
 // hardware address.
 TEST_F(MySqlHostDataSourceTest, get6ByHWaddr) {

+ 6 - 0
src/lib/dhcpsrv/tests/pgsql_host_data_source_unittest.cc

@@ -292,6 +292,12 @@ TEST_F(PgSqlHostDataSourceTest, get6PrefixWithHWaddr) {
     testGetByIPv6(Host::IDENT_HWADDR, true);
 }
 
+// Test verifies that host with IPv6 prefix reservation can be retrieved
+// by subnet id and prefix value.
+TEST_F(PgSqlHostDataSourceTest, get6SubnetPrefix) {
+    testGetBySubnetIPv6();
+}
+
 // Test verifies if a host reservation can be added and later retrieved by
 // hardware address.
 TEST_F(PgSqlHostDataSourceTest, get6ByHWaddr) {

+ 9 - 0
src/lib/dhcpsrv/writable_host_data_source.h

@@ -141,6 +141,15 @@ public:
     virtual HostPtr
     get6(const asiolink::IOAddress& prefix, const uint8_t prefix_len) = 0;
 
+    /// @brief Returns a host connected to the IPv6 subnet and having
+    /// a reservation for a specified IPv6 address or prefix.
+    ///
+    /// @param subnet_id Subnet identifier.
+    /// @param address reserved IPv6 address/prefix.
+    ///
+    /// @return @c Host object using a specified IPv6 address/prefix.
+    virtual HostPtr
+    get6(const SubnetID& subnet_id, const asiolink::IOAddress& address) = 0;
 };
 
 }