]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[5466] Implemented lease4 and lease6 parsing from JSON.
authorMarcin Siodelski <marcin@isc.org>
Tue, 13 Feb 2018 18:55:43 +0000 (19:55 +0100)
committerMarcin Siodelski <marcin@isc.org>
Tue, 13 Feb 2018 18:55:43 +0000 (19:55 +0100)
src/lib/dhcpsrv/lease.cc
src/lib/dhcpsrv/lease.h
src/lib/dhcpsrv/tests/lease_unittest.cc

index 1aa6e28b70e8191f188471a4f8104015e0e0ac67..bb5a81ad6e7bce1b4e09b65db778e74e76d7b9f7 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2017 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2018 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
@@ -6,9 +6,11 @@
 
 #include <dhcpsrv/lease.h>
 #include <util/pointer_util.h>
+#include <boost/scoped_ptr.hpp>
 #include <sstream>
 #include <iostream>
 
+
 using namespace isc::util;
 using namespace isc::data;
 using namespace std;
@@ -50,6 +52,24 @@ Lease::typeToText(Lease::Type type) {
    }
 }
 
+Lease::Type
+Lease::textToType(const std::string& text) {
+    if (text == "V4") {
+        return (TYPE_V4);
+
+    } else  if (text == "IA_NA") {
+        return (TYPE_NA);
+
+    } else if (text == "IA_TA") {
+        return (TYPE_TA);
+
+    } else if (text == "IA_PD") {
+        return (TYPE_PD);
+    }
+
+    isc_throw(BadValue, "unsupported lease type " << text);
+}
+
 std::string
 Lease::basicStatesToText(const uint32_t state) {
     switch (state) {
@@ -95,6 +115,154 @@ Lease::hasIdenticalFqdn(const Lease& other) const {
             fqdn_rev_ == other.fqdn_rev_);
 }
 
+void
+Lease::fromElementCommon(const LeasePtr& lease, const data::ConstElementPtr& element) {
+    if (!element) {
+        isc_throw(BadValue, "parsed lease data is null");
+    }
+
+    if (element->getType() != Element::map) {
+        isc_throw(BadValue, "parsed lease data is not a JSON map");
+    }
+
+
+    if (!lease) {
+        isc_throw(Unexpected, "pointer to parsed lease is null");
+    }
+
+    bool is_v6 = static_cast<bool>(boost::dynamic_pointer_cast<Lease6>(lease));
+
+    // IP address.
+    ConstElementPtr ip_address = element->get("ip-address");
+    if (!ip_address || (ip_address->getType() != Element::string)) {
+        isc_throw(BadValue, "ip-address not present in the parsed lease"
+                  " or it is not a string");
+    }
+
+    boost::scoped_ptr<asiolink::IOAddress> io_address;
+    try {
+        io_address.reset(new asiolink::IOAddress(ip_address->stringValue()));
+
+    } catch (const std::exception& ex) {
+        isc_throw(BadValue, "invalid IP address " << ip_address->stringValue()
+                  << " in the parsed lease");
+    }
+
+    if (!is_v6 && !io_address->isV4()) {
+        isc_throw(BadValue, "address " << *io_address << " it not an IPv4 address");
+
+    } else if (is_v6 && !io_address->isV6()) {
+        isc_throw(BadValue, "address " << *io_address << " it not an IPv6 address");
+    }
+
+    lease->addr_ = *io_address;
+
+    // Subnet identifier.
+    ConstElementPtr subnet_id = element->get("subnet-id");
+    if (!subnet_id || (subnet_id->getType() != Element::integer)) {
+        isc_throw(BadValue, "subnet-id not present in the parsed lease"
+                  " or it is not a number");
+    }
+
+    if (subnet_id->intValue() <= 0) {
+        isc_throw(BadValue, "subnet-id " << subnet_id->intValue() << " is not"
+                            << " a positive integer");
+    }
+
+    lease->subnet_id_ = SubnetID(subnet_id->intValue());
+
+    // Hardware address.
+    ConstElementPtr hw_address = element->get("hw-address");
+    if (hw_address) {
+        if (hw_address->getType() != Element::string) {
+            isc_throw(BadValue, "hw-address is not a string in the parsed lease");
+
+        }
+
+        try {
+            HWAddr parsed_hw_address = HWAddr::fromText(hw_address->stringValue());
+            lease->hwaddr_.reset(new HWAddr(parsed_hw_address.hwaddr_, HTYPE_ETHER));
+
+        } catch (const std::exception& ex) {
+            isc_throw(BadValue, "invalid hardware address "
+                      << hw_address->stringValue() << " in the parsed lease");
+        }
+
+    } else if (!is_v6) {
+        isc_throw(BadValue, "hw-address not present in the parsed lease");
+    }
+
+    // cltt
+    ConstElementPtr cltt = element->get("cltt");
+    if (!cltt || (cltt->getType() != Element::integer)) {
+        isc_throw(BadValue, "cltt is not present in the parsed lease"
+                  " or it is not a number");
+    }
+
+    if (cltt->intValue() <= 0) {
+        isc_throw(BadValue, "cltt " << cltt->intValue() << " is not a"
+                  " positive integer in the parsed lease");
+    }
+
+    lease->cltt_ = static_cast<time_t>(cltt->intValue());
+
+    // valid lifetime
+    ConstElementPtr valid_lifetime = element->get("valid-lft");
+    if (!valid_lifetime || (valid_lifetime->getType() != Element::integer)) {
+        isc_throw(BadValue, "valid-lft is not present in the parsed lease"
+                  " or it is not a number");
+    }
+
+    if (valid_lifetime->intValue() < 0) {
+        isc_throw(BadValue, "valid-lft " << valid_lifetime->intValue()
+                  << " is negative in the parsed lease");
+    }
+
+    lease->valid_lft_ = valid_lifetime->intValue();
+
+    // fqdn-fwd
+    ConstElementPtr fqdn_fwd = element->get("fqdn-fwd");
+    if (!fqdn_fwd || fqdn_fwd->getType() != Element::boolean) {
+        isc_throw(BadValue, "fqdn-fwd is not present in the parsed lease"
+                  " or it is not a boolean value");
+    }
+
+    lease->fqdn_fwd_ = fqdn_fwd->boolValue();
+
+    // fqdn-fwd
+    ConstElementPtr fqdn_rev = element->get("fqdn-rev");
+    if (!fqdn_rev || (fqdn_rev->getType() != Element::boolean)) {
+        isc_throw(BadValue, "fqdn-rev is not present in the parsed lease"
+                  " or it is not a boolean value");
+    }
+
+    lease->fqdn_rev_ = fqdn_rev->boolValue();
+
+    // hostname
+    ConstElementPtr hostname = element->get("hostname");
+    if (!hostname || (hostname->getType() != Element::string)) {
+        isc_throw(BadValue, "hostname is not present in the parsed lease"
+                  " or it is not a string value");
+    }
+
+    lease->hostname_ = hostname->stringValue();
+
+    // state
+    ConstElementPtr state = element->get("state");
+    if (!state || (state->getType() != Element::integer)) {
+        isc_throw(BadValue, "state is not present in the parsed lease"
+                  " or it is not a number");
+    }
+
+    if ((state->intValue() < 0) || (state->intValue() > Lease::STATE_EXPIRED_RECLAIMED)) {
+        isc_throw(BadValue, "state " << state->intValue()
+                  << " must be in range [0.."
+                  << Lease::STATE_EXPIRED_RECLAIMED << "]");
+    }
+
+    lease->state_ = state->intValue();
+}
+
 Lease4::Lease4(const Lease4& other)
     : Lease(other.addr_, other.t1_, other.t2_, other.valid_lft_,
             other.subnet_id_, other.cltt_, other.fqdn_fwd_,
@@ -245,6 +413,33 @@ Lease4::toElement() const {
     return (map);
 }
 
+Lease4Ptr
+Lease4::fromElement(const ConstElementPtr& element) {
+    Lease4Ptr lease(new Lease4());
+
+    // Extract common lease properties into the lease.
+    fromElementCommon(boost::dynamic_pointer_cast<Lease>(lease), element);
+
+    // Client identifier is IPv4 specific.
+    ConstElementPtr client_id = element->get("client-id");
+    if (client_id) {
+        if (client_id->getType() != Element::string) {
+            isc_throw(BadValue, "client identifier is not a string in the"
+                      " parsed lease");
+        }
+
+        try {
+            lease->client_id_ = ClientId::fromText(client_id->stringValue());
+
+        } catch (const std::exception& ex) {
+            isc_throw(BadValue, "invalid client identifier "
+                      << client_id->stringValue() << " in the parsed lease");
+        }
+    }
+
+    return (lease);
+}
+
 Lease6::Lease6(Lease::Type type, const isc::asiolink::IOAddress& addr,
                DuidPtr duid, uint32_t iaid, uint32_t preferred, uint32_t valid,
                uint32_t t1, uint32_t t2, SubnetID subnet_id,
@@ -417,6 +612,83 @@ Lease6::toElement() const {
     return (map);
 }
 
+Lease6Ptr
+Lease6::fromElement(const data::ConstElementPtr& element) {
+    Lease6Ptr lease(new Lease6());
+
+    // Extract common lease properties into the lease.
+    fromElementCommon(boost::dynamic_pointer_cast<Lease>(lease), element);
+
+    // lease type
+    ConstElementPtr lease_type = element->get("type");
+    if (!lease_type || (lease_type->getType() != Element::string)) {
+        isc_throw(BadValue, "type is not present in the parsed lease"
+                  " or it is not a string value");
+    }
+
+    lease->type_ = textToType(lease_type->stringValue());
+
+    // prefix length
+    ConstElementPtr prefix_len = element->get("prefix-len");
+    if (lease->type_ == Lease::TYPE_PD) {
+        if (!prefix_len || (prefix_len->getType() != Element::integer)) {
+            isc_throw(BadValue, "prefix-len is not present in the parsed lease"
+                      " or it is not a number");
+        }
+
+        if ((prefix_len->intValue() < 1) || (prefix_len->intValue() > 128)) {
+            isc_throw(BadValue, "prefix-len " << prefix_len->intValue()
+                      << " must be in range of [1..128]");
+        }
+
+        lease->prefixlen_ = static_cast<uint8_t>(prefix_len->intValue());
+    }
+
+    // IAID
+    ConstElementPtr iaid = element->get("iaid");
+    if (!iaid || (iaid->getType() != Element::integer)) {
+        isc_throw(BadValue, "iaid is not present in the parsed lease"
+                  " or it is not a number");
+    }
+
+    if (iaid->intValue() < 0) {
+        isc_throw(BadValue, "iaid " << iaid->intValue() << " must not be negative");
+    }
+
+    lease->iaid_ = static_cast<uint32_t>(iaid->intValue());
+
+    // DUID
+    ConstElementPtr duid = element->get("duid");
+    if (!duid || (duid->getType() != Element::string)) {
+        isc_throw(BadValue, "duid not present in the parsed lease"
+                  " or it is not a string");
+    }
+
+    try {
+        DUID parsed_duid = DUID::fromText(duid->stringValue());
+        lease->duid_.reset(new DUID(parsed_duid.getDuid()));
+
+    } catch (const std::exception& ex) {
+        isc_throw(BadValue, "invalid DUID "
+                  << duid->stringValue() << " in the parsed lease");
+    }
+
+    // preferred lifetime
+    ConstElementPtr preferred_lft = element->get("preferred-lft");
+    if (!preferred_lft || (preferred_lft->getType() != Element::integer)) {
+        isc_throw(BadValue, "preferred-lft is not present in the parsed lease"
+                  " or is not a number");
+    }
+
+    if (preferred_lft->intValue() < 0) {
+        isc_throw(BadValue, "preferred-lft " << preferred_lft->intValue()
+                  << " must not be negative");
+    }
+
+    lease->preferred_lft_ = static_cast<uint32_t>(preferred_lft->intValue());
+
+    return (lease);
+}
 
 std::ostream&
 operator<<(std::ostream& os, const Lease& lease) {
index 9527f352b7e6ed3b66653ae528e97cf33f15618d..d8869b5af8a6a8c67dbdd282aca6e1acf1577b4f 100644 (file)
@@ -22,6 +22,11 @@ namespace dhcp {
 /// because subnet.h needs Lease::Type, so it includes lease.h
 typedef uint32_t SubnetID;
 
+struct Lease;
+
+/// @brief Pointer to the lease object.
+typedef boost::shared_ptr<Lease> LeasePtr;
+
 /// @brief a common structure for IPv4 and IPv6 leases
 ///
 /// This structure holds all information that is common between IPv4 and IPv6
@@ -41,6 +46,13 @@ struct Lease : public isc::data::CfgToElement {
     /// @return text description
     static std::string typeToText(Type type);
 
+    /// @brief Converts type name to the actual type.
+    ///
+    /// @param text lease type as text.
+    /// @return converted type.
+    /// @throw BadValue if the text contains unsupported value.
+    static Type textToType(const std::string& text);
+
     /// @name Common lease states constants.
     //@{
     ///
@@ -208,8 +220,27 @@ struct Lease : public isc::data::CfgToElement {
     ///
     /// @param probation_period lease lifetime will be set to this value
     virtual void decline(uint32_t probation_period) = 0;
+
+protected:
+
+    /// @brief Sets common (for v4 and v6) properties of the lease object.
+    ///
+    /// This method is called by the @c fromElement methods of the @c Lease
+    /// class derivations.
+    ///
+    /// @param [out] lease pointer to the lease object for which common
+    /// properties should be set.
+    /// @param element pointer to the data element object to be parsed.
+    static void fromElementCommon(const LeasePtr& lease,
+                                  const data::ConstElementPtr& element);
+
 };
 
+struct Lease4;
+
+/// @brief Pointer to a Lease4 structure.
+typedef boost::shared_ptr<Lease4> Lease4Ptr;
+
 /// @brief Structure that holds a lease for IPv4 address
 ///
 /// For performance reasons it is a simple structure, not a class. If we chose
@@ -404,18 +435,27 @@ struct Lease4 : public Lease {
     /// @brief Return the JSON representation of a lease
     virtual isc::data::ElementPtr toElement() const;
 
+    /// @brief Returns pointer to the IPv4 lease created from JSON
+    /// representation.
+    ///
+    /// @param element pointer to the data element object to be parsed.
+    /// @return Pointer to the created lease.
+    static Lease4Ptr fromElement(const data::ConstElementPtr& element);
+
     /// @todo: Add DHCPv4 failover related fields here
 };
 
-/// @brief Pointer to a Lease4 structure.
-typedef boost::shared_ptr<Lease4> Lease4Ptr;
-
 /// @brief A collection of IPv4 leases.
 typedef std::vector<Lease4Ptr> Lease4Collection;
 
 /// @brief A shared pointer to the collection of IPv4 leases.
 typedef boost::shared_ptr<Lease4Collection> Lease4CollectionPtr;
 
+struct Lease6;
+
+/// @brief Pointer to a Lease6 structure.
+typedef boost::shared_ptr<Lease6> Lease6Ptr;
+
 /// @brief Structure that holds a lease for IPv6 address and/or prefix
 ///
 /// For performance reasons it is a simple structure, not a class. If we chose
@@ -542,10 +582,14 @@ struct Lease6 : public Lease {
 
     /// @brief Return the JSON representation of a lease
     virtual isc::data::ElementPtr toElement() const;
-};
 
-/// @brief Pointer to a Lease6 structure.
-typedef boost::shared_ptr<Lease6> Lease6Ptr;
+    /// @brief Returns pointer to the IPv6 lease created from JSON
+    /// representation.
+    ///
+    /// @param element pointer to the data element object to be parsed.
+    /// @return Pointer to the created lease.
+    static Lease6Ptr fromElement(const data::ConstElementPtr& element);
+};
 
 /// @brief Pointer to a const Lease6 structure.
 typedef boost::shared_ptr<const Lease6> ConstLease6Ptr;
index 827ccd98dbd3e417669dd8199619f3c89eafd1fe..a81719e50341419e2df092e7fbda3afd1d020484 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2013-2017 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2018 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
@@ -50,6 +50,47 @@ Lease4 createLease4(const std::string& hostname, const bool fqdn_fwd,
     return (lease);
 }
 
+/// @brief Tests that an exception is thrown when one of the lease
+/// parameters is missing or invalid during lease parsing.
+///
+/// If the tested parameter is mandatory the test first removes the
+/// specified parameter from the JSON structure and tries to re-create
+/// the lease, expecting an error.
+/// Next, the test sets invalid value for this parameter and also
+/// expected an error.
+///
+/// @tparam LeaseType @c Lease4 or @c Lease6.
+/// @tparam ValueType type of the invalid value to be used for the
+/// specified parameter.
+/// @param lease_as_json a string contains lease in a JSON format.
+/// @param parameter_name name of the lease parameter to be tested.
+/// @param invalid_value invalid parameter value.
+/// @param mandatory boolean value indicating if the parameter is
+/// mandatory.
+template<typename LeaseType, typename ValueType>
+void testInvalidElement(const std::string& lease_as_json,
+                        const std::string& parameter_name,
+                        ValueType invalid_value,
+                        const bool mandatory = true) {
+    ElementPtr lease;
+
+    // If the parameter is mandatory, check that the exception is
+    // thrown if it is missing.
+    if (mandatory) {
+        ASSERT_NO_THROW(lease = Element::fromJSON(lease_as_json));
+        lease->remove(parameter_name);
+        EXPECT_THROW(LeaseType::fromElement(lease), BadValue)
+            << "test failed for " << parameter_name;
+    }
+
+    // Set invalid value and expect an error.
+    ASSERT_NO_THROW(lease = Element::fromJSON(lease_as_json));
+    lease->set(parameter_name, Element::create(invalid_value));
+    EXPECT_THROW(LeaseType::fromElement(lease), BadValue)
+        << "test failed for " << parameter_name
+        << " and invalid value " << invalid_value;
+}
+
 /// @brief Fixture class used in Lease4 testing.
 class Lease4Test : public ::testing::Test {
 public:
@@ -467,6 +508,78 @@ TEST_F(Lease4Test, toElement) {
     runToElementTest<Lease4>(expected, lease);
 }
 
+// Verify that the Lease4 can be created from JSON.
+TEST_F(Lease4Test, fromElement) {
+    std::string json = "{"
+        "\"client-id\": \"17:34:e2:ff:09:92:54\","
+        "\"cltt\": 12345678,"
+        "\"fqdn-fwd\": true,"
+        "\"fqdn-rev\": true,"
+        "\"hostname\": \"urania.example.org\","
+        "\"hw-address\": \"08:00:2b:02:3f:4e\","
+        "\"ip-address\": \"192.0.2.3\","
+        "\"state\": 0,"
+        "\"subnet-id\": 789,"
+        "\"valid-lft\": 3600 "
+        "}";
+
+    Lease4Ptr lease;
+    ASSERT_NO_THROW(lease = Lease4::fromElement(Element::fromJSON(json)));
+
+    ASSERT_TRUE(lease);
+
+    EXPECT_EQ("192.0.2.3", lease->addr_.toText());
+    EXPECT_EQ(789, static_cast<uint32_t>(lease->subnet_id_));
+    ASSERT_TRUE(lease->hwaddr_);
+    EXPECT_EQ("hwtype=1 08:00:2b:02:3f:4e", lease->hwaddr_->toText());
+    ASSERT_TRUE(lease->client_id_);
+    EXPECT_EQ("17:34:e2:ff:09:92:54", lease->client_id_->toText());
+    EXPECT_EQ(12345678, lease->cltt_);
+    EXPECT_EQ(3600, lease->valid_lft_);
+    EXPECT_TRUE(lease->fqdn_fwd_);
+    EXPECT_TRUE(lease->fqdn_rev_);
+    EXPECT_EQ("urania.example.org", lease->hostname_);
+    EXPECT_EQ(Lease::STATE_DEFAULT, lease->state_);
+}
+
+// Test that specifying invalid values for a lease or not specifying
+// mandatory lease parameters causes an error while parsing the lease.
+TEST_F(Lease4Test, fromElementInvalidValues) {
+    // Create valid configuration. We use it as a base from which we will
+    // be removing some of the parameters and some values will be selectively
+    // modified.
+    std::string json = "{"
+        "\"client-id\": \"17:34:e2:ff:09:92:54\","
+        "\"cltt\": 12345678,"
+        "\"fqdn-fwd\": true,"
+        "\"fqdn-rev\": true,"
+        "\"hostname\": \"urania.example.org\","
+        "\"hw-address\": \"08:00:2b:02:3f:4e\","
+        "\"ip-address\": \"192.0.2.3\","
+        "\"state\": 0,"
+        "\"subnet-id\": 789,"
+        "\"valid-lft\": 3600 "
+        "}";
+
+    // Test invalid parameter values and missing parameters.
+    testInvalidElement<Lease4>(json, "client-id", std::string("rock"), false);
+    testInvalidElement<Lease4>(json, "cltt", std::string("xyz"));
+    testInvalidElement<Lease4>(json, "cltt", -1, false);
+    testInvalidElement<Lease4>(json, "fqdn-fwd", 123);
+    testInvalidElement<Lease4>(json, "fqdn-rev", std::string("foo"));
+    testInvalidElement<Lease4, bool>(json, "hostname", true);
+    testInvalidElement<Lease4>(json, "hw-address", "01::00::");
+    testInvalidElement<Lease4>(json, "hw-address", 1234, false);
+    testInvalidElement<Lease4, long int>(json, "ip-address", 0xFF000201);
+    testInvalidElement<Lease4>(json, "ip-address", "2001:db8:1::1", false);
+    testInvalidElement<Lease4>(json, "state", std::string("xyz"));
+    testInvalidElement<Lease4>(json, "state", 1234, false);
+    testInvalidElement<Lease4>(json, "subnet-id", std::string("xyz"));
+    testInvalidElement<Lease4>(json, "subnet-id", -5, false);
+    testInvalidElement<Lease4>(json, "valid-lft", std::string("xyz"));
+    testInvalidElement<Lease4>(json, "valid-lft", -3, false);
+}
+
 // Verify that decline() method properly clears up specific fields.
 TEST_F(Lease4Test, decline) {
 
@@ -1013,6 +1126,144 @@ TEST(Lease6Test, toElementPrefix) {
     EXPECT_FALSE(l->contains("hw-address"));
 }
 
+// Verify that the IA_NA can be created from JSON.
+TEST(Lease6Test, fromElementNA) {
+    std::string json = "{"
+        "\"cltt\": 12345678,"
+        "\"duid\": \"00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f\","
+        "\"fqdn-fwd\": false,"
+        "\"fqdn-rev\": false,"
+        "\"hostname\": \"urania.example.org\","
+        "\"hw-address\": \"08:00:2b:02:3f:4e\","
+        "\"iaid\": 123456,"
+        "\"ip-address\": \"2001:db8::1\","
+        "\"preferred-lft\": 400,"
+        "\"state\": 1,"
+        "\"subnet-id\": 5678,"
+        "\"type\": \"IA_NA\","
+        "\"valid-lft\": 800"
+        "}";
+
+    Lease6Ptr lease;
+    ASSERT_NO_THROW(lease = Lease6::fromElement(Element::fromJSON(json)));
+
+    ASSERT_TRUE(lease);
+
+    EXPECT_EQ("2001:db8::1", lease->addr_.toText());
+    EXPECT_EQ(5678, static_cast<uint32_t>(lease->subnet_id_));
+    ASSERT_TRUE(lease->hwaddr_);
+    EXPECT_EQ("hwtype=1 08:00:2b:02:3f:4e", lease->hwaddr_->toText());
+    EXPECT_EQ(12345678, lease->cltt_);
+    EXPECT_EQ(800, lease->valid_lft_);
+    EXPECT_FALSE(lease->fqdn_fwd_);
+    EXPECT_FALSE(lease->fqdn_rev_);
+    EXPECT_EQ("urania.example.org", lease->hostname_);
+    EXPECT_EQ(Lease::STATE_DECLINED, lease->state_);
+
+    // IPv6 specific properties.
+    EXPECT_EQ(Lease::TYPE_NA, lease->type_);
+    EXPECT_EQ(0, lease->prefixlen_);
+    EXPECT_EQ(123456, lease->iaid_);
+    ASSERT_TRUE(lease->duid_);
+    EXPECT_EQ("00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f", lease->duid_->toText());
+    EXPECT_EQ(400, lease->preferred_lft_);
+}
+
+// Verify that the IA_NA can be created from JSON.
+TEST(Lease6Test, fromElementPD) {
+    std::string json = "{"
+        "\"cltt\": 12345678,"
+        "\"duid\": \"00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f\","
+        "\"fqdn-fwd\": false,"
+        "\"fqdn-rev\": false,"
+        "\"hostname\": \"urania.example.org\","
+        "\"hw-address\": \"08:00:2b:02:3f:4e\","
+        "\"iaid\": 123456,"
+        "\"ip-address\": \"3000::\","
+        "\"preferred-lft\": 400,"
+        "\"prefix-len\": 32,"
+        "\"state\": 0,"
+        "\"subnet-id\": 1234,"
+        "\"type\": \"IA_PD\","
+        "\"valid-lft\": 600"
+        "}";
+
+    Lease6Ptr lease;
+    ASSERT_NO_THROW(lease = Lease6::fromElement(Element::fromJSON(json)));
+
+    ASSERT_TRUE(lease);
+
+    EXPECT_EQ("3000::", lease->addr_.toText());
+    EXPECT_EQ(1234, static_cast<uint32_t>(lease->subnet_id_));
+    ASSERT_TRUE(lease->hwaddr_);
+    EXPECT_EQ("hwtype=1 08:00:2b:02:3f:4e", lease->hwaddr_->toText());
+    EXPECT_EQ(12345678, lease->cltt_);
+    EXPECT_EQ(600, lease->valid_lft_);
+    EXPECT_FALSE(lease->fqdn_fwd_);
+    EXPECT_FALSE(lease->fqdn_rev_);
+    EXPECT_EQ("urania.example.org", lease->hostname_);
+    EXPECT_EQ(Lease::STATE_DEFAULT , lease->state_);
+
+    // IPv6 specific properties.
+    EXPECT_EQ(Lease::TYPE_PD, lease->type_);
+    EXPECT_EQ(32, static_cast<int>(lease->prefixlen_));
+    EXPECT_EQ(123456, lease->iaid_);
+    ASSERT_TRUE(lease->duid_);
+    EXPECT_EQ("00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f", lease->duid_->toText());
+    EXPECT_EQ(400, lease->preferred_lft_);
+}
+
+// Test that specifying invalid values for a lease or not specifying
+// mandatory lease parameters causes an error while parsing the lease.
+TEST(Lease6Test, fromElementInvalidValues) {
+    // Create valid configuration. We use it as a base from which we will
+    // be removing some of the parameters and some values will be selectively
+    // modified.
+    std::string json = "{"
+        "\"cltt\": 12345678,"
+        "\"duid\": \"00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f\","
+        "\"fqdn-fwd\": false,"
+        "\"fqdn-rev\": false,"
+        "\"hostname\": \"urania.example.org\","
+        "\"hw-address\": \"08:00:2b:02:3f:4e\","
+        "\"iaid\": 123456,"
+        "\"ip-address\": \"3000::\","
+        "\"preferred-lft\": 400,"
+        "\"prefix-len\": 32,"
+        "\"state\": 0,"
+        "\"subnet-id\": 1234,"
+        "\"type\": \"IA_PD\","
+        "\"valid-lft\": 600"
+        "}";
+
+    // Test invalid parameter values and missing parameters.
+    testInvalidElement<Lease6>(json, "cltt", std::string("xyz"));
+    testInvalidElement<Lease6>(json, "cltt", -1, false);
+    testInvalidElement<Lease6>(json, "duid", "01::00::");
+    testInvalidElement<Lease6>(json, "duid", 1234, false);
+    testInvalidElement<Lease6>(json, "fqdn-fwd", 123);
+    testInvalidElement<Lease6>(json, "fqdn-rev", std::string("foo"));
+    testInvalidElement<Lease6, bool>(json, "hostname", true);
+    testInvalidElement<Lease6>(json, "hw-address", "01::00::", false);
+    testInvalidElement<Lease6>(json, "hw-address", 1234, false);
+    testInvalidElement<Lease6>(json, "iaid", std::string("1234"));
+    testInvalidElement<Lease6>(json, "iaid", -1);
+    testInvalidElement<Lease6, long int>(json, "ip-address", 0xFF000201);
+    testInvalidElement<Lease6>(json, "ip-address", "192.0.2.0", false);
+    testInvalidElement<Lease6>(json, "preferred-lft", std::string("1234"));
+    testInvalidElement<Lease6>(json, "preferred-lft", -1, false);
+    testInvalidElement<Lease6>(json, "prefix-len", std::string("1234"));
+    testInvalidElement<Lease6>(json, "prefix-len", 130);
+    testInvalidElement<Lease6>(json, "state", std::string("xyz"));
+    testInvalidElement<Lease6>(json, "state", 1234, false);
+    testInvalidElement<Lease6>(json, "subnet-id", std::string("xyz"));
+    testInvalidElement<Lease6>(json, "subnet-id", -5, false);
+    testInvalidElement<Lease6>(json, "type", std::string("IA_XY"));
+    testInvalidElement<Lease6>(json, "type", -3, false);
+    testInvalidElement<Lease6>(json, "valid-lft", std::string("xyz"));
+    testInvalidElement<Lease6>(json, "valid-lft", -3, false);
+}
+
 // Verify that the lease states are correctly returned in the textual format.
 TEST(Lease6Test, stateToText) {
     EXPECT_EQ("default", Lease6::statesToText(Lease::STATE_DEFAULT));