Browse Source

[3562] Added negative test scenarios for ipv6 host reservations.

Marcin Siodelski 10 years ago
parent
commit
0ed7f6f05e

+ 34 - 0
src/lib/dhcpsrv/host.cc

@@ -15,6 +15,7 @@
 #include <dhcpsrv/host.h>
 #include <util/strutil.h>
 #include <exceptions/exceptions.h>
+#include <sstream>
 
 namespace isc {
 namespace dhcp {
@@ -42,6 +43,17 @@ IPv6Resrv::set(const asiolink::IOAddress& prefix, const uint8_t prefix_len) {
     prefix_len_ = prefix_len;
 }
 
+std::string
+IPv6Resrv::toText() const {
+    std::ostringstream s;
+    s << prefix_;
+    // For PD, append prefix length.
+    if (getType() == TYPE_PD) {
+        s << "/" << static_cast<int>(prefix_len_);
+    }
+    return (s.str());
+}
+
 bool
 IPv6Resrv::operator==(const IPv6Resrv& other) const {
     return (prefix_ == other.prefix_ &&
@@ -158,6 +170,12 @@ Host::setIPv4Reservation(const asiolink::IOAddress& address) {
 
 void
 Host::addReservation(const IPv6Resrv& reservation) {
+    // Check if it is not duplicating existing reservation.
+    if (hasReservation(reservation)) {
+        isc_throw(isc::InvalidOperation, "failed on attempt to add a duplicated"
+                  " host reservation for " << reservation.toText());
+    }
+    // Add it.
     ipv6_reservations_.insert(IPv6ResrvTuple(reservation.getType(),
                                              reservation));
 }
@@ -167,6 +185,22 @@ Host::getIPv6Reservations(const IPv6Resrv::Type& type) const {
     return (ipv6_reservations_.equal_range(type));
 }
 
+bool
+Host::hasReservation(const IPv6Resrv& reservation) const {
+    IPv6ResrvRange reservations = getIPv6Reservations(reservation.getType());
+    if (std::distance(reservations.first, reservations.second) > 0) {
+        for (IPv6ResrvIterator it = reservations.first;
+             it != reservations.second; ++it) {
+            if (it->second == reservation) {
+                return (true);
+            }
+        }
+    }
+
+    // No matching reservations found.
+    return (false);
+}
+
 void
 Host::addClientClassInternal(ClientClasses& classes,
                              const std::string& class_name) {

+ 11 - 0
src/lib/dhcpsrv/host.h

@@ -91,6 +91,9 @@ public:
     /// multicast address or the prefix length is greater than 128.
     void set(const asiolink::IOAddress& prefix, const uint8_t prefix_len);
 
+    /// @brief Returns information about the reservation in the textual format.
+    std::string toText() const;
+
     /// @brief Equality operator.
     ///
     /// @param other Reservation to compare to.
@@ -340,6 +343,14 @@ public:
     /// the specified type.
     IPv6ResrvRange getIPv6Reservations(const IPv6Resrv::Type& type) const;
 
+    /// @brief Checks if specified IPv6 reservation exists for the host.
+    ///
+    /// @param reservation A reservation to be checked for the host.
+    ///
+    /// @return true if the reservation already exists for the host, false
+    /// otherwise.
+    bool hasReservation(const IPv6Resrv& reservation) const;
+
     /// @brief Sets new hostname.
     ///
     /// @param hostname New hostname.

+ 73 - 0
src/lib/dhcpsrv/tests/host_reservation_parser_unittest.cc

@@ -271,5 +271,78 @@ TEST_F(HostReservationParserTest, dhcp6IPv4Address) {
     EXPECT_THROW(parser.build(config_element), DhcpConfigError);
 }
 
+// This test verifies that the configuration parser throws an exception
+// when empty address has been specified.
+TEST_F(HostReservationParserTest, dhcp6NullAddress) {
+    std::string config = "{ \"duid\": \"01:02:03:04:05:06:07:08:09:0A\","
+        "\"ip-addresses\": [ \"\" ],"
+        "\"prefixes\": [ ] }";
+
+    ElementPtr config_element = Element::fromJSON(config);
+
+    HostReservationParser6 parser(SubnetID(12));
+    EXPECT_THROW(parser.build(config_element), DhcpConfigError);
+}
+
+// This test verifies that the configuration parser throws an exception
+// when invalid prefix length is specified.
+TEST_F(HostReservationParserTest, dhcp6InvalidPrefixLength) {
+    std::string config = "{ \"duid\": \"01:02:03:04:05:06:07:08:09:0A\","
+        "\"prefixes\": [ \"2001:db8:1::/abc\" ] }";
+
+    ElementPtr config_element = Element::fromJSON(config);
+
+    HostReservationParser6 parser(SubnetID(12));
+    EXPECT_THROW(parser.build(config_element), DhcpConfigError);
+}
+
+// This test verifies that the configuration parser throws an exception
+// when empty prefix is specified.
+TEST_F(HostReservationParserTest, dhcp6NullPrefix) {
+    std::string config = "{ \"duid\": \"01:02:03:04:05:06:07:08:09:0A\","
+        "\"prefixes\": [ \"/64\" ] }";
+
+    ElementPtr config_element = Element::fromJSON(config);
+
+    HostReservationParser6 parser(SubnetID(12));
+    EXPECT_THROW(parser.build(config_element), DhcpConfigError);
+}
+
+// This test verifies that the configuration parser throws an exception
+// when only slash is specified for the prefix..
+TEST_F(HostReservationParserTest, dhcp6NullPrefix2) {
+    std::string config = "{ \"duid\": \"01:02:03:04:05:06:07:08:09:0A\","
+        "\"prefixes\": [ \"/\" ] }";
+
+    ElementPtr config_element = Element::fromJSON(config);
+
+    HostReservationParser6 parser(SubnetID(12));
+    EXPECT_THROW(parser.build(config_element), DhcpConfigError);
+}
+
+// This test verifies that the configuration parser throws an exception
+// when the same address is reserved twice.
+TEST_F(HostReservationParserTest, dhcp6DuplicatedAddress) {
+    std::string config = "{ \"duid\": \"01:02:03:04:05:06:07:08:09:0A\","
+        "\"ip-addresses\": [ \"2001:db8:1::1\", \"2001:db8:1::1\" ] }";
+
+    ElementPtr config_element = Element::fromJSON(config);
+
+    HostReservationParser6 parser(SubnetID(12));
+    EXPECT_THROW(parser.build(config_element), DhcpConfigError);
+}
+
+// This test verifies that the configuration parser throws an exception
+// when the same prefix is reserved twice.
+TEST_F(HostReservationParserTest, dhcp6DuplicatedPrefix) {
+    std::string config = "{ \"duid\": \"01:02:03:04:05:06:07:08:09:0A\","
+        "\"prefixes\": [ \"2001:db8:0101::/64\", \"2001:db8:0101::/64\" ] }";
+
+    ElementPtr config_element = Element::fromJSON(config);
+
+    HostReservationParser6 parser(SubnetID(12));
+    EXPECT_THROW(parser.build(config_element), DhcpConfigError);
+}
+
 
 } // end of anonymous namespace

+ 17 - 0
src/lib/dhcpsrv/tests/host_unittest.cc

@@ -42,6 +42,15 @@ TEST(IPv6ResrvTest, constructorPrefix) {
     EXPECT_EQ(IPv6Resrv::TYPE_PD, resrv.getType());
 }
 
+// This test verifies that the toText() function prints correctly.
+TEST(IPv6ResrvTest, toText) {
+    IPv6Resrv resrv_prefix(IOAddress("2001:db8:1::"), 64);
+    EXPECT_EQ("2001:db8:1::/64", resrv_prefix.toText());
+
+    IPv6Resrv resrv_address(IOAddress("2001:db8:111::23"));
+    EXPECT_EQ("2001:db8:111::23", resrv_address.toText());
+}
+
 // This test verifies that invalid prefix is rejected.
 TEST(IPv6ResrvTest, constructorInvalidPrefix) {
     // IPv4 address is invalid for IPv6 reservation.
@@ -339,6 +348,14 @@ TEST(HostTest, addReservations) {
         host->addReservation(IPv6Resrv(IOAddress("2001:db8:1::1")));
     );
 
+    // Check that reservations exist.
+    EXPECT_TRUE(host->hasReservation(IPv6Resrv(IOAddress("2001:db8:1::cafe"))));
+    EXPECT_TRUE(host->hasReservation(IPv6Resrv(IOAddress("2001:db8:1:1::"),
+                                               64)));
+    EXPECT_TRUE(host->hasReservation(IPv6Resrv(IOAddress("2001:db8:1:2::"),
+                                               64)));
+    EXPECT_TRUE(host->hasReservation(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));