From: Francis Dupont Date: Mon, 1 Jul 2024 13:43:47 +0000 (+0200) Subject: [#1387] Checkpoint: added PPexclude X-Git-Tag: Kea-2.7.3~66 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=40790175a6702a42dd0307848bc4b673c757cd43;p=thirdparty%2Fkea.git [#1387] Checkpoint: added PPexclude --- diff --git a/src/lib/dhcpsrv/host.cc b/src/lib/dhcpsrv/host.cc index 5d45192ee4..a1ea354c55 100644 --- a/src/lib/dhcpsrv/host.cc +++ b/src/lib/dhcpsrv/host.cc @@ -86,7 +86,8 @@ AuthKey::operator!=(const AuthKey& other) const { IPv6Resrv::IPv6Resrv(const Type& type, const asiolink::IOAddress& prefix, const uint8_t prefix_len) - : type_(type), prefix_(asiolink::IOAddress("::")), prefix_len_(128) { + : type_(type), prefix_(asiolink::IOAddress("::")), prefix_len_(128), + pd_exclude_option_() { // Validate and set the actual values. set(type, prefix, prefix_len); } @@ -112,19 +113,55 @@ IPv6Resrv::set(const Type& type, const asiolink::IOAddress& prefix, type_ = type; prefix_ = prefix; prefix_len_ = prefix_len; + pd_exclude_option_.reset(); +} + +void +IPv6Resrv::setPDExclude(const asiolink::IOAddress& excluded_prefix, + const uint8_t excluded_prefix_len) { + if (excluded_prefix_len == 0) { + pd_exclude_option_.reset(); + } else { + pd_exclude_option_.reset(new Option6PDExclude(prefix_, prefix_len_, + excluded_prefix, + excluded_prefix_len)); + } } std::string -IPv6Resrv::toText() const { +IPv6Resrv::toText(bool display_pd_exclude_option) const { std::ostringstream s; s << prefix_; // For PD, append prefix length. - if (getType() == TYPE_PD) { + if (type_ == TYPE_PD) { s << "/" << static_cast(prefix_len_); + // If there is a Prefix Exclude option append it. + if (display_pd_exclude_option && pd_exclude_option_) { + s << " (excluded_prefix=" + << pd_exclude_option_->getExcludedPrefix(prefix_, + prefix_len_).toText() + << "/" + << static_cast(pd_exclude_option_->getExcludedPrefixLength()) + << ")"; + } } return (s.str()); } +std::string +IPv6Resrv::PDExcludetoText() const { + if (pd_exclude_option_) { + std::ostringstream s; + s << pd_exclude_option_->getExcludedPrefix(prefix_, + prefix_len_).toText() + << "/" + << static_cast(pd_exclude_option_->getExcludedPrefixLength()); + return (s.str()); + } else { + return (""); + } +} + bool IPv6Resrv::operator==(const IPv6Resrv& other) const { return (type_ == other.type_ && @@ -607,10 +644,22 @@ Host::toElement6() const { // Set reservations (prefixes) const IPv6ResrvRange& pd_resv = getIPv6Reservations(IPv6Resrv::TYPE_PD); resvs = Element::createList(); + bool has_pd_exclude_option(false); BOOST_FOREACH(auto const& resv, pd_resv) { - resvs->add(Element::create(resv.second.toText())); + if (resv.second.getPDExclude()) { + has_pd_exclude_option = true; + } + resvs->add(Element::create(resv.second.toText(false))); } map->set("prefixes", resvs); + // Set Prefix Exclude options. + if (has_pd_exclude_option) { + ElementPtr options = Element::createList(); + BOOST_FOREACH(auto const& resv, pd_resv) { + options->add(Element::create(resv.second.PDExcludetoText())); + } + map->set("excluded-prefixes", options); + } // Set the hostname const std::string& hostname = getHostname(); map->set("hostname", Element::create(hostname)); diff --git a/src/lib/dhcpsrv/host.h b/src/lib/dhcpsrv/host.h index fd9eb7541f..13c98ff364 100644 --- a/src/lib/dhcpsrv/host.h +++ b/src/lib/dhcpsrv/host.h @@ -1,4 +1,4 @@ -// Copyright (C) 2014-2023 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2014-2024 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 @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -157,7 +158,8 @@ private: /// /// The class holds the address and prefix length, a value of 128 /// for the latter implying that the reservation is for a single -/// IPv6 address. +/// IPv6 address. For prefix delegations it includes an optional +/// Prefix Exclude option. class IPv6Resrv { public: @@ -205,8 +207,17 @@ public: return (type_); } + /// @brief Returns the Prefix Exclude option. + /// + /// @return Prefix Exclude option. + Option6PDExcludePtr getPDExclude() const { + return (pd_exclude_option_); + } + /// @brief Sets a new prefix and prefix length. /// + /// @note This removes the Prefix Exclude option when it exists. + /// /// @param type Reservation type: NA or PD. /// @param prefix New prefix. /// @param prefix_len New prefix length. @@ -216,11 +227,29 @@ public: void set(const Type& type, const asiolink::IOAddress& prefix, const uint8_t prefix_len); + /// @brief Sets the Prefix Exclude option. + /// + /// @note excluded_prefix_len == 0 means there's no excluded prefix at all. + /// + /// @param excluded_prefix specifies an excluded prefix as per RFC6603. + /// @param excluded_prefix_len specifies length of an excluded prefix. + void setPDExclude(const asiolink::IOAddress& excluded_prefix, + const uint8_t excluded_prefix_len); + /// @brief Returns information about the reservation in the textual format. - std::string toText() const; + /// + /// @param display_pd_exclude_option When true (default) add the Prefix + /// Exclude option if it exists. + std::string toText(bool display_pd_exclude_option = true) const; + + /// @brief Returns information about the Prefix Exclude option of + /// the reservation the textual format. + std::string PDExcludetoText() const; /// @brief Equality operator. /// + /// @note this compares only type, prefix and prefix length. + /// /// @param other Reservation to compare to. bool operator==(const IPv6Resrv& other) const; @@ -231,9 +260,10 @@ public: private: - Type type_; ///< Reservation type. - asiolink::IOAddress prefix_; ///< Prefix - uint8_t prefix_len_; ///< Prefix length. + Type type_; ///< Reservation type. + asiolink::IOAddress prefix_; ///< Prefix. + uint8_t prefix_len_; ///< Prefix length. + Option6PDExcludePtr pd_exclude_option_; ///< Prefix Exclude option. }; /// @brief Collection of IPv6 reservations for the host. diff --git a/src/lib/dhcpsrv/pool.cc b/src/lib/dhcpsrv/pool.cc index b726cc282e..f7a53f6d8d 100644 --- a/src/lib/dhcpsrv/pool.cc +++ b/src/lib/dhcpsrv/pool.cc @@ -427,7 +427,9 @@ Pool6::toText() const { << static_cast(prefix_len_); if (pd_exclude_option_) { - s << ", excluded_prefix_len=" + s << ", excluded_prefix=" + << pd_exclude_option_->getExcludedPrefix(first_, prefix_len_).toText() + << "/" << static_cast(pd_exclude_option_->getExcludedPrefixLength()); } return (s.str()); diff --git a/src/lib/dhcpsrv/tests/host_unittest.cc b/src/lib/dhcpsrv/tests/host_unittest.cc index 4ac51e112b..f2509f1078 100644 --- a/src/lib/dhcpsrv/tests/host_unittest.cc +++ b/src/lib/dhcpsrv/tests/host_unittest.cc @@ -119,6 +119,48 @@ TEST(IPv6ResrvTest, setPrefix) { isc::BadValue, expected); } +// This test verifies that it is possible to manage prefix exclude option. +TEST(IPv6ResrvTest, setPrefixExcludeOption) { + // Create a reservation using a prefix having a length of 48. + IPv6Resrv resrv(IPv6Resrv::TYPE_PD, IOAddress("2001:db8::"), 48); + + // Add a Prefix Exclude option. + ASSERT_NO_THROW(resrv.setPDExclude(IOAddress("2001:db8:0:1::"), 64)); + Option6PDExcludePtr opt = resrv.getPDExclude(); + EXPECT_TRUE(opt); + EXPECT_EQ("2001:db8:0:1::", + opt->getExcludedPrefix(resrv.getPrefix(), + resrv.getPrefixLen()).toText()); + EXPECT_EQ(64, opt->getExcludedPrefixLength()); + string expected = "2001:db8::/48 (excluded_prefix=2001:db8:0:1::/64)"; + EXPECT_EQ(expected, resrv.toText()); + + // Length 0 means remove. + EXPECT_NO_THROW(resrv.setPDExclude(IOAddress("1.2.3.4"), 0)); + EXPECT_FALSE(resrv.getPDExclude()); + + // Set also removes the option. + EXPECT_NO_THROW(resrv.setPDExclude(IOAddress("2001:db8:0:1::"), 64)); + EXPECT_TRUE(resrv.getPDExclude()); + ASSERT_NO_THROW(resrv.set(IPv6Resrv::TYPE_PD, IOAddress("2001:db8::"), 48)); + EXPECT_FALSE(resrv.getPDExclude()); + + // Error case: the excluded prefix not in the (delegated) prefix. + expected = "excluded prefix 2001:db8:1::/64 must have the same common"; + expected += " prefix part of 48 as the delegated prefix"; + expected += " 2001:db8::/48"; + EXPECT_THROW_MSG(resrv.setPDExclude(IOAddress("2001:db8:1::"), 64), + BadValue, expected); + + // Error case: the excluded prefix length must be less than the + // (delegated) prefix. + expected = "length of the excluded prefix 2001:db8::/48"; + expected += " must be greater than the length of the delegated prefix"; + expected += " 2001:db8::/48"; + EXPECT_THROW_MSG(resrv.setPDExclude(IOAddress("2001:db8::"), 48), + BadValue, expected); +} + // This test checks that the equality operators work fine. TEST(IPv6ResrvTest, equal) { EXPECT_TRUE(IPv6Resrv(IPv6Resrv::TYPE_PD, IOAddress("2001:db8::"), 64) == @@ -1104,8 +1146,9 @@ TEST_F(HostTest, toText) { IOAddress("2001:db8:1::cafe"))); host->addReservation(IPv6Resrv(IPv6Resrv::TYPE_PD, IOAddress("2001:db8:1:1::"), 64)); - host->addReservation(IPv6Resrv(IPv6Resrv::TYPE_PD, - IOAddress("2001:db8:1:2::"), 64)); + IPv6Resrv res(IPv6Resrv::TYPE_PD, IOAddress("2001:db8:1:2::"), 64); + res.setPDExclude(IOAddress("2001:db8:1:2:3::"), 80); + host->addReservation(IPv6Resrv(res)); host->addReservation(IPv6Resrv(IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::1"))); ); @@ -1125,7 +1168,8 @@ TEST_F(HostTest, toText) { " ipv6_reservation0=2001:db8:1::cafe" " ipv6_reservation1=2001:db8:1::1" " ipv6_reservation2=2001:db8:1:1::/64" - " ipv6_reservation3=2001:db8:1:2::/64", + " ipv6_reservation3=2001:db8:1:2::/64" + " (excluded_prefix=2001:db8:1:2:3::/80)", host->toText()); // Reset some of the data and make sure that the output is affected. @@ -1144,6 +1188,7 @@ TEST_F(HostTest, toText) { " ipv6_reservation1=2001:db8:1::1" " ipv6_reservation2=2001:db8:1:1::/64" " ipv6_reservation3=2001:db8:1:2::/64" + " (excluded_prefix=2001:db8:1:2:3::/80)" " negative cached", host->toText()); @@ -1212,14 +1257,12 @@ TEST_F(HostTest, unparse) { IOAddress("192.0.2.3"), "myhost.example.com"))); - // Add 4 reservations: 2 for NAs, 2 for PDs. + // Add 3 reservations: 2 for NAs, 1 for PDs. ASSERT_NO_THROW( host->addReservation(IPv6Resrv(IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::cafe"))); host->addReservation(IPv6Resrv(IPv6Resrv::TYPE_PD, IOAddress("2001:db8:1:1::"), 64)); - host->addReservation(IPv6Resrv(IPv6Resrv::TYPE_PD, - IOAddress("2001:db8:1:2::"), 64)); host->addReservation(IPv6Resrv(IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::1"))); ); @@ -1248,11 +1291,18 @@ TEST_F(HostTest, unparse) { "\"hw-address\": \"01:02:03:04:05:06\", " "\"ip-addresses\": [ \"2001:db8:1::cafe\", \"2001:db8:1::1\" ], " "\"option-data\": [ ], " - "\"prefixes\": [ \"2001:db8:1:1::/64\", \"2001:db8:1:2::/64\" ], " + "\"prefixes\": [ \"2001:db8:1:1::/64\" ], " "\"user-context\": { \"comment\": \"a host reservation\" } " "}", host->toElement6()->str()); + // Add a Prefix Exclude option. + ASSERT_NO_THROW( + IPv6Resrv res(IPv6Resrv::TYPE_PD, IOAddress("2001:db8:1:2::"), 64); + res.setPDExclude(IOAddress("2001:db8:1:2:3::"), 80); + host->addReservation(res); + ); + // Reset some of the data and make sure that the output is affected. host->setHostname(""); host->removeIPv4Reservation(); @@ -1272,6 +1322,7 @@ TEST_F(HostTest, unparse) { EXPECT_EQ("{ " "\"client-classes\": [ ], " + "\"excluded-prefixes\": [ \"\", \"2001:db8:1:2:3::/80\" ], " "\"hostname\": \"\", " "\"hw-address\": \"01:02:03:04:05:06\", " "\"ip-addresses\": [ \"2001:db8:1::cafe\", \"2001:db8:1::1\" ], " diff --git a/src/lib/dhcpsrv/tests/pool_unittest.cc b/src/lib/dhcpsrv/tests/pool_unittest.cc index ee0dced878..7c01098911 100644 --- a/src/lib/dhcpsrv/tests/pool_unittest.cc +++ b/src/lib/dhcpsrv/tests/pool_unittest.cc @@ -473,7 +473,7 @@ TEST(Pool6Test, toText) { Pool6 pool3(IOAddress("2001:db8:1::"), 96, 112, IOAddress("2001:db8:1::1000"), 120); EXPECT_EQ("type=IA_PD, 2001:db8:1::-2001:db8:1::ffff:ffff, delegated_len=112," - " excluded_prefix_len=120", + " excluded_prefix=2001:db8:1::1000/120", pool3.toText()); Pool6 pool4(Lease::TYPE_NA, IOAddress("2001:db8::"),