Browse Source

[3560] Implemented rest of the functions in the Host class.

Also, added documentation to the Host class.
Marcin Siodelski 10 years ago
parent
commit
a3530098c8

+ 23 - 18
src/lib/dhcp/duid.cc

@@ -30,20 +30,20 @@ namespace dhcp {
 
 DUID::DUID(const std::vector<uint8_t>& duid) {
     if (duid.size() > MAX_DUID_LEN) {
-        isc_throw(OutOfRange, "DUID too large");
+        isc_throw(isc::BadValue, "DUID too large");
     }
     if (duid.empty()) {
-        isc_throw(OutOfRange, "Empty DUIDs are not allowed");
+        isc_throw(isc::BadValue, "Empty DUIDs are not allowed");
     }
     duid_ = duid;
 }
 
 DUID::DUID(const uint8_t* data, size_t len) {
     if (len > MAX_DUID_LEN) {
-        isc_throw(OutOfRange, "DUID too large");
+        isc_throw(isc::BadValue, "DUID too large");
     }
     if (len == 0) {
-        isc_throw(OutOfRange, "Empty DUIDs/Client-ids not allowed");
+        isc_throw(isc::BadValue, "Empty DUIDs/Client-ids not allowed");
     }
 
     duid_ = std::vector<uint8_t>(data, data + len);
@@ -58,19 +58,24 @@ DUID::decode(const std::string& text) {
 
     std::ostringstream s;
     for (size_t i = 0; i < split_text.size(); ++i) {
-        // If there are multiple tokens and the current one is empty, it
-        // means that two consecutive colons were specified. This is not
-        // allowed for client identifier.
-        if ((split_text.size() > 1) && split_text[i].empty()) {
-            isc_throw(isc::BadValue, "invalid identifier '" << text << "': "
-                      << " tokens must be separated with a single colon");
-
-        } else if (split_text[i].size() == 1) {
-            s << "0";
-
-        } else if (split_text[i].size() > 2) {
-            isc_throw(isc::BadValue, "invalid identifier '" << text << "'");
+        if (split_text.size() > 1) {
+            // If there are multiple tokens and the current one is empty, it
+            // means that two consecutive colons were specified. This is not
+            // allowed for client identifier.
+            if (split_text[i].empty()) {
+                isc_throw(isc::BadValue, "invalid identifier '"
+                          << text << "': " << " tokens must be"
+                          " separated with a single colon");
+            } else if (split_text[i].size() > 2) {
+                isc_throw(isc::BadValue, "invalid identifier '"
+                          << text << "'");
+            }
         }
+
+        if (split_text[i].size() % 2) {
+                s << "0";
+        }
+
         s << split_text[i];
     }
 
@@ -133,7 +138,7 @@ bool DUID::operator!=(const DUID& other) const {
 ClientId::ClientId(const std::vector<uint8_t>& clientid)
     : DUID(clientid) {
     if (clientid.size() < MIN_CLIENT_ID_LEN) {
-        isc_throw(OutOfRange, "client-id is too short (" << clientid.size()
+        isc_throw(isc::BadValue, "client-id is too short (" << clientid.size()
                   << "), at least 2 is required");
     }
 }
@@ -142,7 +147,7 @@ ClientId::ClientId(const std::vector<uint8_t>& clientid)
 ClientId::ClientId(const uint8_t *clientid, size_t len)
     : DUID(clientid, len) {
     if (len < MIN_CLIENT_ID_LEN) {
-        isc_throw(OutOfRange, "client-id is too short (" << len
+        isc_throw(isc::BadValue, "client-id is too short (" << len
                   << "), at least 2 is required");
     }
 }

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

@@ -34,14 +34,14 @@ HWAddr::HWAddr()
 HWAddr::HWAddr(const uint8_t* hwaddr, size_t len, uint16_t htype)
     :hwaddr_(hwaddr, hwaddr + len), htype_(htype) {
     if (len > MAX_HWADDR_LEN) {
-        isc_throw(InvalidParameter, "hwaddr length exceeds MAX_HWADDR_LEN");
+        isc_throw(isc::BadValue, "hwaddr length exceeds MAX_HWADDR_LEN");
     }
 }
 
 HWAddr::HWAddr(const std::vector<uint8_t>& hwaddr, uint16_t htype)
     :hwaddr_(hwaddr), htype_(htype) {
     if (hwaddr.size() > MAX_HWADDR_LEN) {
-        isc_throw(InvalidParameter,
+        isc_throw(isc::BadValue,
             "address vector size exceeds MAX_HWADDR_LEN");
     }
 }

+ 15 - 10
src/lib/dhcp/tests/duid_unittest.cc

@@ -84,24 +84,24 @@ TEST(DuidTest, size) {
 
     EXPECT_THROW(
         scoped_ptr<DUID> toolarge1(new DUID(data, MAX_DUID_LEN + 1)),
-        OutOfRange);
+        BadValue);
 
     // that's one too much
     data2.push_back(128);
 
     EXPECT_THROW(
         scoped_ptr<DUID> toolarge2(new DUID(data2)),
-        OutOfRange);
+        BadValue);
 
     // empty duids are not allowed
     vector<uint8_t> empty;
     EXPECT_THROW(
         scoped_ptr<DUID> emptyDuid(new DUID(empty)),
-        OutOfRange);
+        BadValue);
 
     EXPECT_THROW(
         scoped_ptr<DUID> emptyDuid2(new DUID(data, 0)),
-        OutOfRange);
+        BadValue);
 }
 
 // This test verifies if the implementation supports all defined
@@ -222,33 +222,33 @@ TEST(ClientIdTest, size) {
 
     EXPECT_THROW(
         scoped_ptr<ClientId> toolarge1(new ClientId(data, MAX_CLIENT_ID_LEN + 1)),
-        OutOfRange);
+        BadValue);
 
     // that's one too much
     data2.push_back(128);
 
     EXPECT_THROW(
         scoped_ptr<ClientId> toolarge2(new ClientId(data2)),
-        OutOfRange);
+        BadValue);
 
     // empty client-ids are not allowed
     vector<uint8_t> empty;
     EXPECT_THROW(
         scoped_ptr<ClientId> empty_client_id1(new ClientId(empty)),
-        OutOfRange);
+        BadValue);
 
     EXPECT_THROW(
         scoped_ptr<ClientId> empty_client_id2(new ClientId(data, 0)),
-        OutOfRange);
+        BadValue);
 
     // client-id must be at least 2 bytes long
     vector<uint8_t> shorty(1,17); // just a single byte with value 17
     EXPECT_THROW(
         scoped_ptr<ClientId> too_short_client_id1(new ClientId(shorty)),
-        OutOfRange);
+        BadValue);
     EXPECT_THROW(
         scoped_ptr<ClientId> too_short_client_id1(new ClientId(data, 1)),
-        OutOfRange);
+        BadValue);
 }
 
 // This test checks if the comparison operators are sane.
@@ -299,6 +299,11 @@ TEST(ClientIdTest, fromText) {
         cid = ClientId::fromText("00:a:bb:D:ee:EF:ab")
     );
     EXPECT_EQ("00:0a:bb:0d:ee:ef:ab", cid->toText());
+    // ClientId without any colons is allowed.
+    ASSERT_NO_THROW(
+        cid = ClientId::fromText("0010abcdee");
+    );
+    EXPECT_EQ("00:10:ab:cd:ee", cid->toText());
     // Repeated colon sign in the ClientId is not allowed.
     EXPECT_THROW(
         ClientId::fromText("00::bb:D:ee:EF:ab"),

+ 2 - 2
src/lib/dhcp/tests/hwaddr_unittest.cc

@@ -60,10 +60,10 @@ TEST(HWAddrTest, constructor) {
 
     // Check that over the limit data length throws exception 
     EXPECT_THROW(HWAddr(&big_data_vector[0], big_data_vector.size(), HTYPE_ETHER), 
-        InvalidParameter);
+        BadValue);
 
     // Check that over the limit vector throws exception
-    EXPECT_THROW(HWAddr(big_data_vector, HTYPE_ETHER), InvalidParameter);
+    EXPECT_THROW(HWAddr(big_data_vector, HTYPE_ETHER), BadValue);
 }
 
 // This test checks if the comparison operators are sane.

+ 30 - 5
src/lib/dhcpsrv/host.cc

@@ -18,6 +18,18 @@
 namespace isc {
 namespace dhcp {
 
+bool
+IPv6Resrv::operator==(const IPv6Resrv& other) const {
+    return (prefix_ == other.prefix_ &&
+            prefix_len_ == other.prefix_len_);
+}
+
+bool
+IPv6Resrv::operator!=(const IPv6Resrv& other) const {
+    return (prefix_ != other.prefix_ ||
+            prefix_len_ != other.prefix_len_);
+}
+
 Host::Host(const uint8_t* identifier, const size_t identifier_len,
            const IdentifierType& identifier_type,
            const SubnetID ipv4_subnet_id, const SubnetID ipv6_subnet_id,
@@ -27,7 +39,8 @@ Host::Host(const uint8_t* identifier, const size_t identifier_len,
       ipv6_subnet_id_(ipv6_subnet_id), ipv4_reservation_(ipv4_reservation),
       hostname_(hostname), dhcp4_client_classes_(), dhcp6_client_classes_() {
 
-    decodeIdentifier(identifier, identifier_len, identifier_type);
+    // Initialize HWAddr or DUID
+    setIdentifier(identifier, identifier_len, identifier_type);
 }
 
 Host::Host(const std::string& identifier, const std::string& identifier_name,
@@ -38,12 +51,13 @@ Host::Host(const std::string& identifier, const std::string& identifier_name,
       ipv6_subnet_id_(ipv6_subnet_id), ipv4_reservation_(ipv4_reservation),
       hostname_(hostname), dhcp4_client_classes_(), dhcp6_client_classes_() {
 
-    decodeIdentifier(identifier, identifier_name);
+    // Initialize HWAddr or DUID
+    setIdentifier(identifier, identifier_name);
 }
 
 void
-Host::decodeIdentifier(const uint8_t* identifier, const size_t len,
-                       const IdentifierType& type) {
+Host::setIdentifier(const uint8_t* identifier, const size_t len,
+                    const IdentifierType& type) {
     switch (type) {
     case IDENT_HWADDR:
         hw_address_ = HWAddrPtr(new HWAddr(identifier, len, HTYPE_ETHER));
@@ -61,7 +75,7 @@ Host::decodeIdentifier(const uint8_t* identifier, const size_t len,
 }
 
 void
-Host::decodeIdentifier(const std::string& identifier, const std::string& name) {
+Host::setIdentifier(const std::string& identifier, const std::string& name) {
     if (name == "hw-address") {
         hw_address_ = HWAddrPtr(new HWAddr(HWAddr::fromText(identifier)));
         duid_.reset();
@@ -74,6 +88,17 @@ Host::decodeIdentifier(const std::string& identifier, const std::string& name) {
     }
 }
 
+void
+Host::addReservation(const IPv6Resrv& reservation) {
+    ipv6_reservations_.insert(IPv6ResrvTuple(reservation.getType(),
+                                             reservation));
+}
+
+IPv6ResrvRange
+Host::getIPv6Reservations(const IPv6Resrv::Type& type) const {
+    return (ipv6_reservations_.equal_range(type));
+}
+
 
 } // end of namespace isc::dhcp
 } // end of namespace isc

+ 202 - 21
src/lib/dhcpsrv/host.h

@@ -28,123 +28,304 @@
 namespace isc {
 namespace dhcp {
 
+/// @brief IPv6 reservation for a host.
+///
+/// This class represents a reservation of a single IPv6 address or
+/// prefix for the host (in @c Host object). The type of reservation
+/// indicates whether an address on prefix is reserved. The class
+/// holds the information about the prefix length for the prefix
+/// reservations. The prefix length for the address reservation is
+/// set to 128.
 class IPv6Resrv {
 public:
 
+    /// @brief Type of the reservation.
+    ///
+    /// Currently supported types are NA and PD.
     enum Type {
         TYPE_NA,
         TYPE_PD
     };
 
-    IPv6Resrv(const asiolink::IOAddress& address,
+    /// @brief Constructor.
+    ///
+    /// Creates a reservation from the IPv6 address and prefix length
+    /// value. If the prefix length is not specified, the default value
+    /// of 128 is used. This value indicates that the reservation is made
+    /// for an IPv6 address.
+    ///
+    /// @param prefix Address or prefix to be reserved.
+    /// @param prefix_len Prefix length.
+    IPv6Resrv(const asiolink::IOAddress& prefix,
               const uint8_t prefix_len = 128)
-        : address_(address), prefix_len_(prefix_len) {
+        : prefix_(prefix), prefix_len_(prefix_len) {
     }
 
-    const asiolink::IOAddress& getAddress() const {
-        return (address_);
+    /// @brief Returns prefix for the reservation.
+    const asiolink::IOAddress& getPrefix() const {
+        return (prefix_);
     }
 
+    /// @brief Returns prefix length.
     uint8_t getPrefixLen() const {
         return (prefix_len_);
     }
 
+    /// @brief Returns reservation type.
+    ///
+    /// The type of reservation is determined using a prefix length.
+    ///
+    /// @return NA for prefix length equal to 128, PD otherwise.
     Type getType() const {
         return (prefix_len_ == 128 ? TYPE_NA : TYPE_PD);
     }
 
-    void set(const asiolink::IOAddress& address, const uint8_t prefix_len) {
-        address_ = address;
+    /// @brief Sets a new prefix and prefix length.
+    ///
+    /// @param prefix New prefix.
+    /// @param prefix_len New prefix length.
+    void set(const asiolink::IOAddress& prefix, const uint8_t prefix_len) {
+        prefix_ = prefix;
         prefix_len_ = prefix_len;
     }
 
+    /// @brief Equality operator.
+    ///
+    /// @param other Reservation to compare to.
+    bool operator==(const IPv6Resrv& other) const;
+
+    /// @brief Inequality operator.
+    ///
+    /// @param other Reservation to compare to.
+    bool operator!=(const IPv6Resrv& other) const;
+
 private:
-    asiolink::IOAddress address_;
-    uint8_t prefix_len_;
+    asiolink::IOAddress prefix_; ///< Prefix
+    uint8_t prefix_len_;         ///< Prefix length.
 
 };
 
 /// @brief Collection of IPv6 reservations for the host.
 typedef std::multimap<IPv6Resrv::Type, IPv6Resrv> IPv6ResrvCollection;
-typedef IPv6ResrvCollection::iterator IPv6ResrvIterator;
+typedef IPv6ResrvCollection::const_iterator IPv6ResrvIterator;
+typedef std::pair<IPv6Resrv::Type, IPv6Resrv> IPv6ResrvTuple;
 typedef std::pair<IPv6ResrvIterator, IPv6ResrvIterator> IPv6ResrvRange;
 
+/// @brief Represents a device with IPv4 and/or IPv6 reservations.
+///
+/// This class represents a device in the network which can be identified
+/// by the unique property, such as MAC address on the interface or
+/// client identifier (DUID), and for which some resources are statically
+/// assigned:
+/// - IPv4 address which the device obtains when it contacts a DHCPv4 server
+/// - IPv6 address(es) which the device obtains when it contacts a DHCPv6
+/// server
+/// - IPv6 prefix(es) obtained when the device contacts the DHCPv6 server
+/// and requests allocation of prefixes using prefix delegation mechanism
+/// - hostname which is used for dynamic DNS updates for both DHCPv4 and
+/// DHCPv6 exchanges.
+/// - client classes which the client is associated with
+/// - DHCP options specifically configured for the device
+///
+/// Note, that the "host" in this context has a different meaning than the
+/// host construed as device attached to a network with (possibly) multiple
+/// interfaces. For the MAC address based reservations, each interface on a
+/// network device maps to a single @c Host object as each @c Host object
+/// contains at most one MAC address. So, it is possible that a single
+/// device is associated with multiple distinct @c Host objects.
+///
+/// A DHCPv6 DUID is common for all interfaces on a device. Therefore, for
+/// DUID based reservations a @c Host object may represent a network device with
+/// multiple interfaces. However, since @c Host objects are grouped by
+/// subnets to which device's interfaces are connected a single instance of
+/// @c Host object usually defines reservations for a single interface.
+///
+/// The @c Host object combines reservations for both IPv4 and IPv6 resources
+/// to allow for correlation of the information about the dual stack devices
+/// using DHCPv4 and DHCPv6 respectively. For example: both DHCPv4 and DHCPv6
+/// server may use the same database for storing host reservations, so the
+/// information about the DHCPv4 reservations are also available for the
+/// DHCPv6 server and vice versa. Also, this approach allows for reserving
+/// common resources such as host name for DHCPv4 and DHCPv6 clients.
 class Host {
 public:
 
+    /// @brief Type of the host identifier.
+    ///
+    /// Currently hardware address assigned to an interface and the
+    /// DHCPv6 client's DUID are supported.
     enum IdentifierType {
         IDENT_HWADDR,
         IDENT_DUID
     };
 
+    /// @brief Constructor.
+    ///
+    /// Creates @c Host object using an identifier in a binary format. This
+    /// is most useful in cases when the identifier is obtained from the
+    /// database. The constructor will create an instance of the @c HWAddr
+    /// or @c DUID object depending on the identifier type.
+    ///
+    /// @param identifier Pointer to the binary value holding an identifier.
+    /// @param identifier_len Length of the identifier.
+    /// @param identifier_type Type of the identifier (hardware address or
+    /// DUID).
+    /// @param ipv4_subnet_id Identifier of the IPv4 subnet to which the host
+    /// is connected.
+    /// @param ipv6_subnet_id Identifier of the IPv6 subnet to which the host
+    /// is connected.
+    /// @param ipv4_reservation An IPv4 address reserved for the client. If
+    /// this address is set to 0, there is no reservation.
+    /// @param hostname Hostname to be allocated to both DHCPv4 and DHCPv6
+    /// clients. This is empty string if hostname is not allocated.
+    ///
+    /// @throw BadValue if the provided values are invalid. In particular,
+    /// if the identifier is invalid.
     Host(const uint8_t* identifier, const size_t identifier_len,
          const IdentifierType& identifier_type,
          const SubnetID ipv4_subnet_id, const SubnetID ipv6_subnet_id,
          const asiolink::IOAddress& ipv4_reservation,
          const std::string& hostname = "");
 
+    /// @brief Constructor.
+    ///
+    /// Creates @c Host object using an identifier in a textual format. This
+    /// is useful in cases when the reservation is specified in the server
+    /// configuration file, where:
+    /// - MAC address is specified as: "01:02:03:04:05:06"
+    /// - DUID is specified as: "010203040506abcd"
+    ///
+    /// @param identifier Identifier in the textual format. The expected format
+    /// for hardware address and DUID has been shown above.
+    /// @param identifier_name One of "hw-address" or "duid"
+    /// @param ipv4_subnet_id Identifier of the IPv4 subnet to which the host
+    /// is connected.
+    /// @param ipv6_subnet_id Identifier of the IPv6 subnet to which the host
+    /// is connected.
+    /// @param ipv4_reservation An IPv4 address reserved for the client. If
+    /// this address is set to 0, there is no reservation.
+    /// @param hostname Hostname to be allocated to both DHCPv4 and DHCPv6
+    /// clients. This is empty string if hostname is not allocated.
+    ///
+    /// @throw BadValue if the provided values are invalid. In particular,
+    /// if the identifier is invalid.
     Host(const std::string& identifier, const std::string& identifier_name,
          const SubnetID ipv4_subnet_id, const SubnetID ipv6_subnet_id,
          const asiolink::IOAddress& ipv4_reservation,
          const std::string& hostname = "");
 
-    void addReservation(const IPv6Resrv& reservation);
-
-    void setIdentifier(const uint8_t* identifier, const size_t identifier_len,
-                       const IdentifierType& identifier_type);
-
+    /// @brief Replaces currently used identifier with a new identifier.
+    ///
+    /// This method initializes hardware address or DUID (@c hw_address_ or
+    /// @c duid_ respectively). The other (not initialized member) is
+    /// deallocated.
+    ///
+    /// This method is called by @c Host constructor.
+    ///
+    /// @param identifier Pointer to the new identifier in the textual format.
+    /// @param len Length of the identifier that the @c identifier points to.
+    /// @param type Identifier type.
+    ///
+    /// @throw BadValue if the identifier is invalid.
+    void setIdentifier(const uint8_t* identifier, const size_t len,
+                       const IdentifierType& type);
+
+    /// @brief Replaces currently used identifier with a new identifier.
+    ///
+    /// This method initializes hardware address or DUID (@c hw_address_ or
+    /// @c duid_ respectively). The other (not initialized member) is
+    /// deallocated.
+    ///
+    /// This method is called by @c Host constructor.
+    ///
+    /// @param identifier Pointer to the new identifier in the textual format.
+    /// @param name One of "hw-address" or "duid".
+    ///
+    /// @throw BadValue if the identifier is invalid.
     void setIdentifier(const std::string& identifier, const std::string& name);
 
+    /// @brief Returns hardware address for which the reservations are made.
+    ///
+    /// @return Pointer to the @c HWAddr structure or null if the reservation
+    /// is not associated with a hardware address.
     HWAddrPtr getHWAddress() const {
         return (hw_address_);
     }
 
+    /// @brief Retruns DUID for which the reservations are made.
+    ///
+    /// @return Pointer to the @c DUID structure or null if the reservation
+    /// is not associated with a DUID.
     DuidPtr getDuid() const {
         return (duid_);
     }
 
+    /// @brief Returns subnet identifier for IPv4 reservation.
     SubnetID getIPv4SubnetID() const {
         return (ipv4_subnet_id_);
     }
 
+    /// @brief Returns subnet identifier for IPv6 reservations.
     SubnetID getIPv6SubnetID() const {
         return (ipv6_subnet_id_);
     }
 
+    /// @brief Returns reserved IPv4 address.
+    ///
+    /// @return IPv4 address or 0.0.0.0 if no IPv4 reservation specified.
     const asiolink::IOAddress& getIPv4Reservation() const {
         return (ipv4_reservation_);
     }
 
-    IPv6ResrvRange getIPv6Reservations(const IPv6Resrv& type) const;
+    /// @brief Adds new IPv6 reservation.
+    ///
+    /// @param reservation New IPv6 reservation to be appended.
+    void addReservation(const IPv6Resrv& reservation);
+
+    /// @brief Returns IPv6 reservations of a specified type.
+    ///
+    /// @param type Type of the reservations to be returned (NA or PD).
+    ///
+    /// @return A range of iterators pointing to the reservations of
+    /// the specified type.
+    IPv6ResrvRange getIPv6Reservations(const IPv6Resrv::Type& type) const;
 
+    /// @brief Returns reserved hostname.
     const std::string& getHostname() const {
         return (hostname_);
     }
 
+    /// @brief Returns classes which DHCPv4 client is associated with.
     const ClientClasses& getClientClasses4() const {
         return (dhcp4_client_classes_);
     }
 
+    /// @brief Returns classes which DHCPv6 client is associated with.
     const ClientClasses& getClientClasses6() const {
         return (dhcp6_client_classes_);
     }
 
 private:
 
-    void decodeIdentifier(const uint8_t* identifier, const size_t len,
-                          const IdentifierType& type);
-
-    void decodeIdentifier(const std::string& identifier,
-                          const std::string& name);
-
+    /// @brief Pointer to the hardware address associated with the reservations
+    /// for the host.
     HWAddrPtr hw_address_;
+    /// @brief Pointer to the DUID associated with the reservations for the
+    /// host.
     DuidPtr duid_;
+    /// @brief Subnet identifier for the DHCPv4 client.
     SubnetID ipv4_subnet_id_;
+    /// @brief Subnet identifier for the DHCPv6 client.
     SubnetID ipv6_subnet_id_;
+    /// @brief Reserved IPv4 address.
     asiolink::IOAddress ipv4_reservation_;
+    /// @brief Collection of IPv6 reservations for the host.
     IPv6ResrvCollection ipv6_reservations_;
+    /// @brief Name reserved for the host.
     std::string hostname_;
+    /// @brief Collection of classes associated with a DHCPv4 client.
     ClientClasses dhcp4_client_classes_;
+    /// @brief Collection of classes associated with a DHCPv6 client.
     ClientClasses dhcp6_client_classes_;
 };
 

+ 246 - 23
src/lib/dhcpsrv/tests/host_unittest.cc

@@ -24,44 +24,82 @@ using namespace isc::asiolink;
 
 namespace {
 
+// This test verifies that it is possible to create IPv6 address
+// reservation.
 TEST(IPv6ResrvTest, constructorAddress) {
     IPv6Resrv resrv(IOAddress("2001:db8:1::cafe"));
-    EXPECT_EQ("2001:db8:1::cafe", resrv.getAddress().toText());
+    EXPECT_EQ("2001:db8:1::cafe", resrv.getPrefix().toText());
     EXPECT_EQ(128, resrv.getPrefixLen());
     EXPECT_EQ(IPv6Resrv::TYPE_NA, resrv.getType());
 }
 
+// This test verifies that it is possible to create IPv6 prefix
+// reservation.
 TEST(IPv6ResrvTest, constructorPrefix) {
     IPv6Resrv resrv(IOAddress("2001:db8:1::"), 64);
-    EXPECT_EQ("2001:db8:1::", resrv.getAddress().toText());
+    EXPECT_EQ("2001:db8:1::", resrv.getPrefix().toText());
     EXPECT_EQ(64, resrv.getPrefixLen());
     EXPECT_EQ(IPv6Resrv::TYPE_PD, resrv.getType());
 }
 
+// This test verifies that it is possible to modify prefix and its
+// length in an existing reservation.
 TEST(IPv6ResrvTest, setPrefix) {
+    // Create a reservation using an address and prefix length 128.
     IPv6Resrv resrv(IOAddress("2001:db8:1::1"));
-    ASSERT_EQ("2001:db8:1::1", resrv.getAddress().toText());
+    ASSERT_EQ("2001:db8:1::1", resrv.getPrefix().toText());
     ASSERT_EQ(128, resrv.getPrefixLen());
     ASSERT_EQ(IPv6Resrv::TYPE_NA, resrv.getType());
 
+    // Modify the reservation to use a prefix having a length of 48.
     ASSERT_NO_THROW(resrv.set(IOAddress("2001:db8::"), 48));
-    EXPECT_EQ("2001:db8::", resrv.getAddress().toText());
+    EXPECT_EQ("2001:db8::", resrv.getPrefix().toText());
     EXPECT_EQ(48, resrv.getPrefixLen());
     EXPECT_EQ(IPv6Resrv::TYPE_PD, resrv.getType());
 }
 
+// This test checks that the equality operators work fine.
+TEST(IPv6ResrvTest, equal) {
+    EXPECT_TRUE(IPv6Resrv(IOAddress("2001:db8::"), 64) ==
+                IPv6Resrv(IOAddress("2001:db8::"), 64));
+
+    EXPECT_FALSE(IPv6Resrv(IOAddress("2001:db8::"), 64) !=
+                IPv6Resrv(IOAddress("2001:db8::"), 64));
+
+
+    EXPECT_TRUE(IPv6Resrv(IOAddress("2001:db8::1")) ==
+                IPv6Resrv(IOAddress("2001:db8::1")));
+    EXPECT_FALSE(IPv6Resrv(IOAddress("2001:db8::1")) !=
+                 IPv6Resrv(IOAddress("2001:db8::1")));
+
+
+    EXPECT_FALSE(IPv6Resrv(IOAddress("2001:db8::1")) ==
+                 IPv6Resrv(IOAddress("2001:db8::2")));
+    EXPECT_TRUE(IPv6Resrv(IOAddress("2001:db8::1")) !=
+                 IPv6Resrv(IOAddress("2001:db8::2")));
+
+    EXPECT_FALSE(IPv6Resrv(IOAddress("2001:db8::"), 64) ==
+                 IPv6Resrv(IOAddress("2001:db8::"), 48));
+    EXPECT_TRUE(IPv6Resrv(IOAddress("2001:db8::"), 64) !=
+                 IPv6Resrv(IOAddress("2001:db8::"), 48));
+
+}
+
+// This test verfies that it is possible to create a Host object
+// using hardware address in the textual format.
 TEST(HostTest, createFromHWAddrString) {
     boost::scoped_ptr<Host> host;
     ASSERT_NO_THROW(host.reset(new Host("01:02:03:04:05:06", "hw-address",
                                         SubnetID(1), SubnetID(2),
                                         IOAddress("192.0.2.3"),
                                         "somehost.example.org")));
-
+    // The HW address should be set to non-null.
     HWAddrPtr hwaddr = host->getHWAddress();
     ASSERT_TRUE(hwaddr);
 
     EXPECT_EQ("hwtype=1 01:02:03:04:05:06", hwaddr->toText());
 
+    // DUID should be null if hardware address is in use.
     EXPECT_FALSE(host->getDuid());
     EXPECT_EQ(1, host->getIPv4SubnetID());
     EXPECT_EQ(2, host->getIPv6SubnetID());
@@ -79,44 +117,229 @@ TEST(HostTest, createFromHWAddrString) {
                  isc::BadValue);
 }
 
+// This test verifies that it is possible to create Host object using
+// a DUID in the textual format.
 TEST(HostTest, createFromDUIDString) {
     boost::scoped_ptr<Host> host;
-    /*    ASSERT_NO_THROW(host.reset(new Host("a1:b2:c3:d4:e5:06", "duid",
+    ASSERT_NO_THROW(host.reset(new Host("a1:b2:c3:d4:e5:06", "duid",
                                         SubnetID(10), SubnetID(20),
                                         IOAddress("192.0.2.5"),
-                                        "me.example.org"))); */
+                                        "me.example.org")));
+
+    // DUID should be set to non-null value.
+    DuidPtr duid = host->getDuid();
+    ASSERT_TRUE(duid);
+
+    EXPECT_EQ("a1:b2:c3:d4:e5:06", duid->toText());
+
+    // Hardware address must be null if DUID is in use.
+    EXPECT_FALSE(host->getHWAddress());
+
+    EXPECT_EQ(10, host->getIPv4SubnetID());
+    EXPECT_EQ(20, host->getIPv6SubnetID());
+    EXPECT_EQ("192.0.2.5", host->getIPv4Reservation().toText());
+    EXPECT_EQ("me.example.org", host->getHostname());
 
+    // Use invalid DUID.
+    EXPECT_THROW(Host("bogus", "duid", SubnetID(1), SubnetID(2),
+                      IOAddress("192.0.2.3"), "somehost.example.org"),
+                 isc::BadValue);
+
+    // Empty DUID is also not allowed.
     try {
-        host.reset(new Host("a1:b2:c3:d4:e5:06", "duid",
-                                        SubnetID(10), SubnetID(20),
-                                        IOAddress("192.0.2.5"),
-                            "me.example.org"));
+        Host("", "duid", SubnetID(1), SubnetID(2),
+             IOAddress("192.0.2.3"), "somehost.example.org");
     } catch (const std::exception& ex) {
         std::cout << ex.what() << std::endl;
     }
+    EXPECT_THROW(Host("", "duid", SubnetID(1), SubnetID(2),
+                      IOAddress("192.0.2.3"), "somehost.example.org"),
+                 isc::BadValue);
+}
 
-    DuidPtr duid = host->getDuid();
-    ASSERT_TRUE(duid);
+// This test verifies that it is possible to create Host object using
+// hardware address in the binary format.
+TEST(HostTest, createFromHWAddrBinary) {
+    boost::scoped_ptr<Host> host;
+    // Prepare the hardware address in binary format.
+    const uint8_t hwaddr_data[] = {
+        0xaa, 0xab, 0xca, 0xda, 0xbb, 0xee
+    };
+    ASSERT_NO_THROW(host.reset(new Host(hwaddr_data,
+                                        sizeof(hwaddr_data),
+                                        Host::IDENT_HWADDR,
+                                        SubnetID(1), SubnetID(2),
+                                        IOAddress("192.0.2.3"),
+                                        "somehost.example.org")));
+    // Hardware address should be non-null.
+    HWAddrPtr hwaddr = host->getHWAddress();
+    ASSERT_TRUE(hwaddr);
 
-    EXPECT_EQ("a1:b2:c3:d4:e5:06", duid->toText());
+    EXPECT_EQ("hwtype=1 aa:ab:ca:da:bb:ee", hwaddr->toText());
 
-    /*    EXPECT_FALSE(host->getDuid());
+    // DUID should be null if hardware address is in use.
+    EXPECT_FALSE(host->getDuid());
     EXPECT_EQ(1, host->getIPv4SubnetID());
     EXPECT_EQ(2, host->getIPv6SubnetID());
     EXPECT_EQ("192.0.2.3", host->getIPv4Reservation().toText());
     EXPECT_EQ("somehost.example.org", host->getHostname());
+}
 
-    // Use invalid identifier name
-    EXPECT_THROW(Host("01:02:03:04:05:06", "bogus", SubnetID(1), SubnetID(2),
-                      IOAddress("192.0.2.3"), "somehost.example.org"),
-                 isc::BadValue);
+// This test verifies that it is possible to create a Host object using
+// DUID in the binary format.
+TEST(HostTest, createFromDuidBinary) {
+    boost::scoped_ptr<Host> host;
+    // Prepare DUID binary.
+    const uint8_t duid_data[] = {
+        1, 2, 3, 4, 5, 6
+    };
+    ASSERT_NO_THROW(host.reset(new Host(duid_data,
+                                        sizeof(duid_data),
+                                        Host::IDENT_DUID,
+                                        SubnetID(10), SubnetID(20),
+                                        IOAddress("192.0.2.5"),
+                                        "me.example.org")));
+    // DUID should be non null.
+    DuidPtr duid = host->getDuid();
+    ASSERT_TRUE(duid);
 
-    // Use invalid HW address.
-    EXPECT_THROW(Host("010203040506", "hw-address", SubnetID(1), SubnetID(2),
-                      IOAddress("192.0.2.3"), "somehost.example.org"),
-                      isc::BadValue); */
+    EXPECT_EQ("01:02:03:04:05:06", duid->toText());
+
+    // Hardware address should be null if DUID is in use.
+    EXPECT_FALSE(host->getHWAddress());
+    EXPECT_EQ(10, host->getIPv4SubnetID());
+    EXPECT_EQ(20, host->getIPv6SubnetID());
+    EXPECT_EQ("192.0.2.5", host->getIPv4Reservation().toText());
+    EXPECT_EQ("me.example.org", host->getHostname());
+}
+
+// Test that it is possible to replace an identifier for a particular
+// Host instance (HW address -> DUID and vice versa) with a new
+// indentifier in the textual format.
+TEST(HostTest, setIdentifierString) {
+    boost::scoped_ptr<Host> host;
+    ASSERT_NO_THROW(host.reset(new Host("01:02:03:04:05:06", "hw-address",
+                                        SubnetID(1), SubnetID(2),
+                                        IOAddress("192.0.2.3"),
+                                        "me.example.com")));
+    // Initially, there should be a HW address, but not a DUID set.
+    ASSERT_TRUE(host->getHWAddress());
+    ASSERT_FALSE(host->getDuid());
+
+    // Now, use a DUID as identifier.
+    ASSERT_NO_THROW(host->setIdentifier("aabbccddee", "duid"));
+
+    // Verify that the DUID is correct.
+    DuidPtr duid = host->getDuid();
+    ASSERT_TRUE(duid);
+    EXPECT_EQ("aa:bb:cc:dd:ee", duid->toText());
+    // HW address should be not set.
+    EXPECT_FALSE(host->getHWAddress());
+
+    // Now, let's do another way around.
+
+    ASSERT_NO_THROW(host->setIdentifier("09:08:07:06:05:04", "hw-address"));
+
+    // Verify that HW address is correct.
+    HWAddrPtr hw_addr = host->getHWAddress();
+    ASSERT_TRUE(hw_addr);
+    EXPECT_EQ("hwtype=1 09:08:07:06:05:04", hw_addr->toText());
+    // DUID should be not set.
+    EXPECT_FALSE(host->getDuid());
+}
+
+// Test that it is possible to replace an identifier for a particular
+// Host instance (HW address -> DUID and vice versa) with the new
+// identifier in the binary format.
+TEST(HostTest, setIdentifierBinary) {
+    boost::scoped_ptr<Host> host;
+    ASSERT_NO_THROW(host.reset(new Host("01:02:03:04:05:06", "hw-address",
+                                        SubnetID(1), SubnetID(2),
+                                        IOAddress("192.0.2.3"),
+                                        "me.example.com")));
+    // Initially, there should be a HW address, but not a DUID set.
+    ASSERT_TRUE(host->getHWAddress());
+    ASSERT_FALSE(host->getDuid());
+
+    // Now, use a DUID as identifier.
+    const uint8_t duid_data[] = {
+        0xaa, 0xbb, 0xcc, 0xdd, 0xee
+    };
+    ASSERT_NO_THROW(host->setIdentifier(duid_data, sizeof(duid_data),
+                                        Host::IDENT_DUID));
+
+    // Verify that the DUID is correct.
+    DuidPtr duid = host->getDuid();
+    ASSERT_TRUE(duid);
+    EXPECT_EQ("aa:bb:cc:dd:ee", duid->toText());
+    // HW address should be not set.
+    EXPECT_FALSE(host->getHWAddress());
+
+    // Now, let's do another way around.
+
+    const uint8_t hwaddr_data[] = {
+        9, 8, 7, 6, 5, 4
+    };
+    ASSERT_NO_THROW(host->setIdentifier(hwaddr_data, sizeof(hwaddr_data),
+                                        Host::IDENT_HWADDR));
+
+    // Verify that HW address is correct.
+    HWAddrPtr hw_addr = host->getHWAddress();
+    ASSERT_TRUE(hw_addr);
+    EXPECT_EQ("hwtype=1 09:08:07:06:05:04", hw_addr->toText());
+    // DUID should be not set.
+    EXPECT_FALSE(host->getDuid());
 }
 
+/// @brief Checks if the reservation is in the range of reservations.
+///
+/// @param resrv Reservation to be searched for.
+/// @param range Range of reservations returned by the @c Host object
+/// in which the reservation will be searched.
+bool
+reservationExists(const IPv6Resrv& resrv, const IPv6ResrvRange& range) {
+    for (IPv6ResrvIterator it = range.first; it != range.second;
+         ++it) {
+        if (resrv == it->second) {
+            return (true);
+        }
+    }
+    return (false);
+}
+
+// This test verifies that the IPv6 reservations of a different type can
+// be added for the host.
+TEST(HostTest, addReservations) {
+    boost::scoped_ptr<Host> host;
+    ASSERT_NO_THROW(host.reset(new Host("01:02:03:04:05:06", "hw-address",
+                                        SubnetID(1), SubnetID(2),
+                                        IOAddress("192.0.2.3"))));
+
+    // Add 4 reservations: 2 for NAs, 2 for PDs.
+    ASSERT_NO_THROW(
+        host->addReservation(IPv6Resrv(IOAddress("2001:db8:1::cafe")));
+        host->addReservation(IPv6Resrv(IOAddress("2001:db8:1:1::"), 64));
+        host->addReservation(IPv6Resrv(IOAddress("2001:db8:1:2::"), 64));
+        host->addReservation(IPv6Resrv(IOAddress("2001:db8:1::1")));
+    );
+
+    // Get only NA reservations.
+    IPv6ResrvRange addresses = host->getIPv6Reservations(IPv6Resrv::TYPE_NA);
+    ASSERT_EQ(2, std::distance(addresses.first, addresses.second));
+    EXPECT_TRUE(reservationExists(IPv6Resrv(IOAddress("2001:db8:1::cafe")),
+                                  addresses));
+    EXPECT_TRUE(reservationExists(IPv6Resrv(IOAddress("2001:db8:1::1")),
+                                  addresses));
+
+
+    // Get only PD reservations.
+    IPv6ResrvRange prefixes = host->getIPv6Reservations(IPv6Resrv::TYPE_PD);
+    ASSERT_EQ(2, std::distance(prefixes.first, prefixes.second));
+    EXPECT_TRUE(reservationExists(IPv6Resrv(IOAddress("2001:db8:1:1::"), 64),
+                                  prefixes));
+    EXPECT_TRUE(reservationExists(IPv6Resrv(IOAddress("2001:db8:1:2::"), 64),
+                                  prefixes));
+}
 
 
 } // end of anonymous namespace