From: Marcin Siodelski Date: Wed, 27 Jul 2016 21:31:29 +0000 (+0200) Subject: [github24] Completed implementation of the Prefix Exclude option. X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a7c0dfc034fd55f5a54e239deba4a4ec436ee5c6;p=thirdparty%2Fkea.git [github24] Completed implementation of the Prefix Exclude option. --- diff --git a/src/lib/dhcp/option6_pdexclude.cc b/src/lib/dhcp/option6_pdexclude.cc index 6e90e8e4c0..734205d140 100644 --- a/src/lib/dhcp/option6_pdexclude.cc +++ b/src/lib/dhcp/option6_pdexclude.cc @@ -9,15 +9,14 @@ #include #include +#include #include #include #include +#include #include - -#include - -#include +#include #include using namespace std; @@ -29,66 +28,201 @@ using namespace isc::util; namespace isc { namespace dhcp { -Option6PDExclude::Option6PDExclude( - const isc::asiolink::IOAddress& delegated_address, - uint8_t delegated_prefix_length, - const isc::asiolink::IOAddress& excluded_address, - uint8_t excluded_prefix_length) : - Option(V6, D6O_PD_EXCLUDE), delegated_address_(delegated_address), - delegated_prefix_length_(delegated_prefix_length), - excluded_address_(excluded_address), - excluded_prefix_length_(excluded_prefix_length) { +Option6PDExclude::Option6PDExclude(const isc::asiolink::IOAddress& delegated_prefix, + const uint8_t delegated_prefix_length, + const isc::asiolink::IOAddress& excluded_prefix, + const uint8_t excluded_prefix_length) + : Option(V6, D6O_PD_EXCLUDE), + delegated_prefix_(delegated_prefix), + delegated_prefix_length_(delegated_prefix_length), + excluded_prefix_(excluded_prefix), + excluded_prefix_length_(excluded_prefix_length) { + + // Expecting v6 prefixes of sane length. + if (!delegated_prefix_.isV6() || !excluded_prefix_.isV6() || + (delegated_prefix_length_ > 128) || (excluded_prefix_length_ > 128)) { + isc_throw(BadValue, "invalid delegated or excluded prefix values specified: " + << delegated_prefix_ << "/" + << static_cast(delegated_prefix_length_) << ", " + << excluded_prefix_ << "/" + << static_cast(excluded_prefix_length_)); + } + + // Excluded prefix must be longer than the delegated prefix. + if (excluded_prefix_length_ <= delegated_prefix_length_) { + isc_throw(BadValue, "length of the excluded prefix " + << excluded_prefix_ << "/" + << static_cast(excluded_prefix_length_) + << " must be greater than the length of the" + " delegated prefix " << delegated_prefix_ << "/" + << static_cast(delegated_prefix_length_)); + } + + // Both prefixes must share common part with a length equal to the + // delegated prefix length. + std::vector delegated_prefix_bytes = delegated_prefix_.toBytes(); + boost::dynamic_bitset delegated_prefix_bits(delegated_prefix_bytes.rbegin(), + delegated_prefix_bytes.rend()); + + std::vector excluded_prefix_bytes = excluded_prefix_.toBytes(); + boost::dynamic_bitset excluded_prefix_bits(excluded_prefix_bytes.rbegin(), + excluded_prefix_bytes.rend()); + + + // See RFC6603, section 4.2: assert(p1>>s == p2>>s) + const uint8_t delta = 128 - delegated_prefix_length; + + if ((delegated_prefix_bits >> delta) != (excluded_prefix_bits >> delta)) { + isc_throw(BadValue, "excluded prefix " + << excluded_prefix_ << "/" + << static_cast(excluded_prefix_length_) + << " must have the same common prefix part of " + << static_cast(delegated_prefix_length) + << " as the delegated prefix " + << delegated_prefix_ << "/" + << static_cast(delegated_prefix_length_)); + } + +} + +Option6PDExclude::Option6PDExclude(const isc::asiolink::IOAddress& delegated_prefix, + const uint8_t delegated_prefix_length, + OptionBufferConstIter begin, + OptionBufferConstIter end) + : Option(V6, D6O_PD_EXCLUDE), + delegated_prefix_(delegated_prefix), + delegated_prefix_length_(delegated_prefix_length), + excluded_prefix_(IOAddress::IPV6_ZERO_ADDRESS()), + excluded_prefix_length_(0) { + unpack(begin, end); } -void Option6PDExclude::pack(isc::util::OutputBuffer& buf) const { +OptionPtr +Option6PDExclude::clone() const { + return (cloneInternal()); +} + +void +Option6PDExclude::pack(isc::util::OutputBuffer& buf) const { // Header = option code and length. packHeader(buf); - buf.writeData(&excluded_prefix_length_, sizeof(excluded_prefix_length_)); - - std::vector excluded_address_bytes = excluded_address_.toBytes(); - boost::dynamic_bitset bits(excluded_address_bytes.rbegin(), excluded_address_bytes.rend()); - bits = bits << delegated_prefix_length_; + // Excluded prefix length is always 1 byte long field. + buf.writeUint8(excluded_prefix_length_); - const uint8_t subtractedPrefixesOctetLength = getSubtractedPrefixesOctetLength(); - for (uint8_t i = 0U; i < subtractedPrefixesOctetLength; i++) { - const boost::dynamic_bitset tmp = bits >> 120; + // Retrieve entire prefix and convert it to bit representation. + std::vector excluded_prefix_bytes = excluded_prefix_.toBytes(); + boost::dynamic_bitset bits(excluded_prefix_bytes.rbegin(), + excluded_prefix_bytes.rend()); - uint8_t val = static_cast(tmp.to_ulong()); + // Shifting prefix by delegated prefix length leaves us with only a + // subnet id part of the excluded prefix. + bits = bits << delegated_prefix_length_; - //Zero padded bits follow when excluded_prefix_length_ is not divided exactly by 8 - if (i == subtractedPrefixesOctetLength - 1U) { - uint8_t subtractedPrefixesBitLength = excluded_prefix_length_ - - delegated_prefix_length_; - uint8_t zeroPaddingBitLength = (8 - (subtractedPrefixesBitLength % 8)) % 8; - val <<= zeroPaddingBitLength; + // Calculate subnet id length. + const uint8_t subnet_id_length = getSubnetIDLength(); + for (uint8_t i = 0; i < subnet_id_length; ++i) { + // Retrieve bit representation of the current byte. + const boost::dynamic_bitset first_byte = bits >> 120; + // Convert it to a numeric value. + uint8_t val = static_cast(first_byte.to_ulong()); + + // Zero padded bits follow when excluded_prefix_length_ is not divisible by 8. + if (i == subnet_id_length - 1) { + uint8_t length_delta = excluded_prefix_length_ - delegated_prefix_length_; + uint8_t mask = 0xFF; + mask <<= (8 - (length_delta % 8)); + val &= mask; } - bits = bits << 8; - buf.writeData(&val, sizeof(val)); + // Store calculated value in a buffer. + buf.writeUint8(val); + + // Go to the next byte. + bits <<= 8; } } -void Option6PDExclude::unpack(OptionBufferConstIter begin, - OptionBufferConstIter end) { - delegated_prefix_length_ = 0; - excluded_prefix_length_ = *begin; - begin += sizeof(uint8_t); - delegated_address_ = IOAddress::IPV6_ZERO_ADDRESS(); - excluded_address_ = IOAddress::IPV6_ZERO_ADDRESS(); +void +Option6PDExclude::unpack(OptionBufferConstIter begin, + OptionBufferConstIter end) { + + // At this point we don't know the excluded prefix length, but the + // minimum requirement is that reminder of this option includes the + // excluded prefix length and at least 1 byte of the IPv6 subnet id. + if (std::distance(begin, end) < 2) { + isc_throw(BadValue, "truncated Prefix Exclude option"); + } + + // We can safely read the excluded prefix length and move forward. + excluded_prefix_length_ = *begin++; + + // We parsed the excluded prefix length so we can now determine the + // size of the IPv6 subnet id. The reminder of the option should + // include data of that size. If the option size is lower than the + // subnet id length we report an error. + const unsigned int subnet_id_length = getSubnetIDLength(); + if (subnet_id_length > std::distance(begin, end)) { + isc_throw(BadValue, "truncated Prefix Exclude option, expected " + "IPv6 subnet id length is " << subnet_id_length); + } + + // Get binary representation of the delegated prefix. + std::vector delegated_prefix_bytes = delegated_prefix_.toBytes(); + // We need to calculate how many bytes include the useful data and assign + // zeros to remaining bytes (beyond the prefix length). + const uint8_t bytes_length = (delegated_prefix_length_ / 8) + + static_cast(delegated_prefix_length_ % 8 != 0); + std::fill(delegated_prefix_bytes.begin() + bytes_length, + delegated_prefix_bytes.end(), 0); + + // Convert the delegated prefix to bit format. + boost::dynamic_bitset bits(delegated_prefix_bytes.rbegin(), + delegated_prefix_bytes.rend()); + + // Convert subnet id to bit format. + std::vector subnet_id_bytes(begin, end); + boost::dynamic_bitset subnet_id_bits(subnet_id_bytes.rbegin(), + subnet_id_bytes.rend()); + + // Subnet id parsed, proceed to the end of the option. begin = end; + + // Concatenate the delegated prefix with subnet id. The resulting prefix + // is an excluded prefix in bit format. + for (int i = subnet_id_bits.size() - 1; i >= 0; --i) { + bits.set(128 - delegated_prefix_length_ - subnet_id_bits.size() + i, + subnet_id_bits.test(i)); + } + + // Convert the prefix to binary format. + std::vector bytes(V6ADDRESS_LEN); + boost::to_block_range(bits, bytes.rbegin()); + + // And create a prefix object from bytes. + excluded_prefix_ = IOAddress::fromBytes(AF_INET6, &bytes[0]); +} + +uint16_t +Option6PDExclude::len() const { + return (getHeaderLen() + sizeof(excluded_prefix_length_) + + getSubnetIDLength()); } -uint16_t Option6PDExclude::len() const { - return getHeaderLen() + sizeof(excluded_prefix_length_) - + getSubtractedPrefixesOctetLength(); +std::string +Option6PDExclude::toText(int indent) const { + std::ostringstream s; + s << headerToText(indent) << ": "; + s << excluded_prefix_ << "/" + << static_cast(excluded_prefix_length_); + return (s.str()); } -uint8_t Option6PDExclude::getSubtractedPrefixesOctetLength() const { - // Promote what is less than 8 bits to 1 octet. - uint8_t subtractedPrefixesBitLength = excluded_prefix_length_ - - delegated_prefix_length_ - 1; - uint8_t subtractedPrefixesOctetLength = (subtractedPrefixesBitLength / 8) + 1; - return subtractedPrefixesOctetLength; +uint8_t +Option6PDExclude::getSubnetIDLength() const { + uint8_t subnet_id_length_bits = excluded_prefix_length_ - + delegated_prefix_length_ - 1; + uint8_t subnet_id_length = (subnet_id_length_bits / 8) + 1; + return (subnet_id_length); } } // end of namespace isc::dhcp diff --git a/src/lib/dhcp/option6_pdexclude.h b/src/lib/dhcp/option6_pdexclude.h index b83a9450e7..20d27d980b 100644 --- a/src/lib/dhcp/option6_pdexclude.h +++ b/src/lib/dhcp/option6_pdexclude.h @@ -9,26 +9,52 @@ #ifndef OPTION6_PDEXCLUDE_H #define OPTION6_PDEXCLUDE_H -#include - #include +#include #include +#include namespace isc { namespace dhcp { -/// @brief DHCPv6 Option class for handling list of IPv6 addresses. +/// @brief DHCPv6 option class representing Prefix Exclude Option (RFC 6603). /// -/// This class handles a list of IPv6 addresses. An example of such option -/// is dns-servers option. It can also be used to handle single address. +/// This class represents DHCPv6 Prefix Exclude option (67). This option is +/// carried in the IA Prefix option and it conveys a single prefix which is +/// used by the delegating router to communicate with a requesting router on +/// the requesting router's uplink. This prefix is not used on the +/// requesting router's downlinks (is excluded from other delegated prefixes). class Option6PDExclude: public Option { - public: - Option6PDExclude(const isc::asiolink::IOAddress& delegated_address, - uint8_t delegated_prefix_length, - const isc::asiolink::IOAddress& excluded_address, - uint8_t excluded_prefix_length); + /// @brief Constructor. + /// + /// @param delegated_prefix Delagated prefix. + /// @param delegated_prefix_length Delegated prefix length. + /// @param excluded_prefix Excluded prefix. + /// @param excluded_prefix_length Excluded prefix length. + /// + /// @throw BadValue if prefixes are invalid, if excluded prefix length + /// is not greater than delegated prefix length or if common parts of + /// prefixes does not match. + Option6PDExclude(const isc::asiolink::IOAddress& delegated_prefix, + const uint8_t delegated_prefix_length, + const isc::asiolink::IOAddress& excluded_prefix, + const uint8_t excluded_prefix_length); + + /// @brief Constructor, creates option instance from part of the buffer. + /// + /// This constructor is mostly used to parse Prefix Exclude options in the + /// received messages. + /// + /// @param begin Lower bound of the buffer to create option from. + /// @param end Upper bound of the buffer to create option from. + Option6PDExclude(const isc::asiolink::IOAddress& delegated_prefix, + const uint8_t delegated_prefix_length, + OptionBufferConstIter begin, OptionBufferConstIter end); + + /// @brief Copies this option and returns a pointer to the copy. + virtual OptionPtr clone() const; /// @brief Writes option in wire-format to a buffer. /// @@ -36,9 +62,12 @@ public: /// byte after stored option (that is useful for writing options one after /// another). /// - /// @param buf pointer to a buffer + /// The format of the option includes excluded prefix length specified as + /// a number of bits. It also includes IPv6 subnet ID field which is + /// computed from the delegated and excluded prefixes, according to the + /// section 4.2 of RFC 6603. /// - /// @throw BadValue Universe of the option is neither V4 nor V6. + /// @param [out] buf Pointer to a buffer. virtual void pack(isc::util::OutputBuffer& buf) const; /// @brief Parses received buffer. @@ -53,56 +82,52 @@ public: /// @return length of the option virtual uint16_t len() const; - /// @brief Returns the address of the delegated address space. + /// @brief Returns Prefix Exclude option in textual format. /// - /// @return address of delegated address space - isc::asiolink::IOAddress getDelegatedAddress() const { - return delegated_address_; + /// @param ident Number of spaces to be inserted before the text. + virtual std::string toText(int indent = 0) const; + + /// @brief Returns delegated prefix. + isc::asiolink::IOAddress getDelegatedPrefix() const { + return (delegated_prefix_); } - /// @brief Returns the prefix length of the delegated address space. - /// - /// @return prefix length of delegated address space + /// @brief Returns delegated prefix length. uint8_t getDelegatedPrefixLength() const { - return delegated_prefix_length_; + return (delegated_prefix_length_); } - /// @brief Returns the address of the excluded address space. - /// - /// @return address of excluded address space - isc::asiolink::IOAddress getExcludedAddress() const { - return excluded_address_; + /// @brief Returns excluded prefix. + isc::asiolink::IOAddress getExcludedPrefix() const { + return (excluded_prefix_); } - /// @brief Returns the prefix length of the excluded address space. - /// - /// @return prefix length of excluded address space + /// @brief Returns excluded prefix length. uint8_t getExcludedPrefixLength() const { - return excluded_prefix_length_; + return (excluded_prefix_length_); } -protected: - /// @brief Returns the prefix length of the excluded prefix. +private: + + /// @brief Returns IPv6 subnet ID length in octets. /// - /// @return prefix length of excluded prefix - uint8_t getSubtractedPrefixesOctetLength() const; + /// The IPv6 subnet ID length is between 1 and 16 octets. + uint8_t getSubnetIDLength() const; - /// @brief The address and prefix length identifying the delegated IPV6 - /// prefix. - /// { - isc::asiolink::IOAddress delegated_address_; + /// @brief Holds delegated prefix. + isc::asiolink::IOAddress delegated_prefix_; + + /// @brief Holds delegated prefix length, uint8_t delegated_prefix_length_; - /// } - /// @brief The address and prefix length identifying the excluded IPV6 - /// prefix. - /// { - isc::asiolink::IOAddress excluded_address_; + /// @brief Holds excluded prefix. + isc::asiolink::IOAddress excluded_prefix_; + + /// @brief Holds excluded prefix length. uint8_t excluded_prefix_length_; - /// } }; -/// @brief Pointer to the @c Option6PDExclude object. +/// @brief Pointer to the @ref Option6PDExclude object. typedef boost::shared_ptr Option6PDExcludePtr; } // isc::dhcp namespace diff --git a/src/lib/dhcp/tests/option6_pdexclude_unittest.cc b/src/lib/dhcp/tests/option6_pdexclude_unittest.cc index 5203cbed10..b7dfa62dfa 100644 --- a/src/lib/dhcp/tests/option6_pdexclude_unittest.cc +++ b/src/lib/dhcp/tests/option6_pdexclude_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC") // // Author: Andrei Pavel // @@ -8,9 +8,11 @@ #include -#include #include +#include +#include #include +#include #include using namespace isc; @@ -19,59 +21,155 @@ using namespace asiolink; namespace { -const IOAddress empty("::"); -const IOAddress beef("2001:db8:dead:beef::"); // /48 prefix length -const IOAddress cafe("2001:db8:dead:cafe::"); // /48 prefix length -const IOAddress beef01("2001:db8:dead:beef::01"); // /56 prefix length +// Prefix constants used in unit tests. +const IOAddress v4("192.0.2.0"); +const IOAddress bee0("2001:db8:dead:bee0::"); +const IOAddress beef("2001:db8:dead:beef::"); +const IOAddress cafe("2001:db8:dead:cafe::"); +const IOAddress beef01("2001:db8:dead:beef::01"); -// Description +// This test verifies that the constructor sets parameters appropriately. TEST(Option6PDExcludeTest, constructor) { Option6PDExclude option = Option6PDExclude(beef, 56, beef01, 60); - EXPECT_EQ(option.getDelegatedAddress(), beef); - EXPECT_EQ(option.getDelegatedPrefixLength(), 56); - EXPECT_EQ(option.getExcludedAddress(), beef01); - EXPECT_EQ(option.getExcludedPrefixLength(), 60); - EXPECT_EQ(option.len(), Option::OPTION6_HDR_LEN + - /* excluded_prefix_length_ AKA prefix-len is 1B */ 1 + - /* [delegated_prefix_length_ - excluded_prefix_length_](bytes) */ 1); + EXPECT_EQ(beef, option.getDelegatedPrefix()); + EXPECT_EQ(56, option.getDelegatedPrefixLength()); + EXPECT_EQ(beef01, option.getExcludedPrefix()); + EXPECT_EQ(60, option.getExcludedPrefixLength()); + // Total length is a sum of option header length, excluded prefix + // length (always 1 byte) and delegated prefix length - excluded prefix + // length rounded to bytes. + EXPECT_EQ(Option::OPTION6_HDR_LEN + 1 + 1, option.len()); + + // v4 prefix is not accepted. + EXPECT_THROW(Option6PDExclude(v4, 56, beef01, 64), BadValue); + EXPECT_THROW(Option6PDExclude(beef, 56, v4, 64), BadValue); + // Length greater than 128 is not accepted. + EXPECT_THROW(Option6PDExclude(beef, 128, beef01, 129), BadValue); + // Excluded prefix length must be greater than delegated prefix length. + EXPECT_THROW(Option6PDExclude(beef, 56, beef01, 56), BadValue); + // Both prefixes shifted by 56 must be equal (see RFC6603, section 4.2). + EXPECT_THROW(Option6PDExclude(cafe, 56, beef01, 64), BadValue); } -TEST(Option6PDExcludeTest, packing_and_unpacking) { - EXPECT_NO_THROW(isc_throw(Exception, "Not implemented yet.")); - - /* - OptionBuffer data(option.getData()); - +// This test verifies that on-wire format of the Prefix Exclude option is +// created properly. +TEST(Option6PDExcludeTest, pack) { + // Expected wire format of the option. + const uint8_t expected_data[] = { + 0x00, 0x43, // option code 67 + 0x00, 0x02, // option length 2 + 0x3F, 0x70 // excluded prefix length 59 + subnet id + }; + std::vector expected_vec(expected_data, + expected_data + sizeof(expected_data)); + // Generate wire format of the option. util::OutputBuffer buf(128); - option.pack(buf); + Option6PDExcludePtr option; + ASSERT_NO_THROW(option.reset(new Option6PDExclude(IOAddress("2001:db8:dead:bee0::"), + 59, + IOAddress("2001:db8:dead:beef::"), + 63))); + ASSERT_NO_THROW(option->pack(buf)); + + // Check that size matches. + ASSERT_EQ(expected_vec.size(), buf.getLength()); + + // Check that the generated wire format is correct. + const uint8_t* data = static_cast(buf.getData()); + std::vector vec(data, data + buf.getLength()); + ASSERT_TRUE(std::equal(vec.begin(), vec.end(), expected_vec.begin())); +} - Option6PDExclude unpackedOption(empty, 0, empty, 0); +// This test verifies parsing option wire format with subnet id of +// 1 byte. +TEST(Option6PDExcludeTest, unpack1ByteSubnetId) { + const uint8_t data[] = { + 0x00, 0x43, // option code 67 + 0x00, 0x02, // option length 2 + 0x40, 0x78 // excluded prefix length 60 + subnet id + }; + std::vector vec(data, data + sizeof(data)); + + // Parse option. + Option6PDExcludePtr option; + ASSERT_NO_THROW( + option.reset(new Option6PDExclude(IOAddress("2001:db8:dead:bee0::1"), + 59, vec.begin() + 4, vec.end())) + ); + + // Make sure that the option has been parsed correctly. + EXPECT_EQ("2001:db8:dead:bee0::1", option->getDelegatedPrefix().toText()); + EXPECT_EQ(59, static_cast(option->getDelegatedPrefixLength())); + EXPECT_EQ("2001:db8:dead:beef::", option->getExcludedPrefix().toText()); + EXPECT_EQ(64, static_cast(option->getExcludedPrefixLength())); +} + +// This test verifies parsing option wire format with subnet id of +// 1 bytes. +TEST(Option6PDExcludeTest, unpack2ByteSubnetId) { + const uint8_t data[] = { + 0x00, 0x43, // option code 67 + 0x00, 0x02, // option length + 0x40, 0xbe, 0xef // excluded prefix length 60 + subnet id + }; + std::vector vec(data, data + sizeof(data)); + + // Parse option. + Option6PDExcludePtr option; + ASSERT_NO_THROW( + option.reset(new Option6PDExclude(IOAddress("2001:db8:dead::"), + 48, vec.begin() + 4, vec.end())) + ); + + // Make sure that the option has been parsed correctly. + EXPECT_EQ("2001:db8:dead::", option->getDelegatedPrefix().toText()); + EXPECT_EQ(48, static_cast(option->getDelegatedPrefixLength())); + EXPECT_EQ("2001:db8:dead:beef::", option->getExcludedPrefix().toText()); + EXPECT_EQ(64, static_cast(option->getExcludedPrefixLength())); +} - unpackedOption.unpack(data.begin(), data.end()); +// This test verifies that errors are reported when option buffer contains +// invalid option data. +TEST(Option6PDExcludeTest, unpackErrors) { + const uint8_t data[] = { + 0x00, 0x43, + 0x00, 0x02, + 0x40, 0x78 + }; + std::vector vec(data, data + sizeof(data)); + + // Option has no IPv6 subnet id. + EXPECT_THROW(Option6PDExclude(IOAddress("2001:db8:dead:bee0::"), + 59, vec.begin() + 4, vec.end() - 1), + BadValue); + + // Option has IPv6 subnet id of 1 byte, but it should have 2 bytes. + EXPECT_THROW(Option6PDExclude(IOAddress("2001:db8:dead::"), 48, + vec.begin() + 4, vec.end()), + BadValue); +} - EXPECT_EQ(option.getDelegatedAddress(), - unpackedOption.getDelegatedAddress()); - EXPECT_EQ(option.getDelegatedPrefixLength(), - unpackedOption.getDelegatedPrefixLength()); - EXPECT_EQ(option.getExcludedAddress(), unpackedOption.getExcludedAddress()); - EXPECT_EQ(option.getExcludedPrefixLength(), - unpackedOption.getExcludedPrefixLength()); - //*/ +// This test verifies conversion of the Prefix Exclude option to the +// textual format. +TEST(Option6PDExcludeTest, toText) { + Option6PDExclude option(bee0, 59, beef, 64); + EXPECT_EQ("type=00067, len=00002: 2001:db8:dead:beef::/64", + option.toText()); } -TEST(Option6PDExcludeTest, pool) { - EXPECT_NO_THROW(isc_throw(Exception, "Not implemented yet.")); - - /* - Pool6Ptr pool6Ptr = Pool6Ptr(new Pool6(Lease::TYPE_PD, beef, cafe)); - ASSERT_TRUE(pool6Ptr); - ASSERT_GT(pool6Ptr->getPrefixExcludedLength(), 0); - OptionPtr opt( - new Option6PDExclude((*l)->addr_, (*l)->prefixlen_, - pool->getPrefixExcluded(), - pool->getPrefixExcludedLength())); - //*/ +// This test verifies calculation of the Prefix Exclude option length. +TEST(Option6PDExcludeTest, len) { + Option6PDExcludePtr option; + // The IPv6 subnet id is 2 bytes long. Hence the total length is + // 2 bytes (option code) + 2 bytes (option length) + 1 byte + // (excluded prefix length) + 2 bytes (IPv6 subnet id) = 7 bytes. + ASSERT_NO_THROW(option.reset(new Option6PDExclude(bee0, 48, beef, 64))); + EXPECT_EQ(7, option->len()); + + // IPv6 subnet id is 1 byte long. The total length is 6. + ASSERT_NO_THROW(option.reset(new Option6PDExclude(bee0, 59, beef, 64))); + EXPECT_EQ(6, option->len()); } } // anonymous namespace