}
PrefixRange::PrefixRange(const asiolink::IOAddress& prefix, const uint8_t length, const uint8_t delegated)
- : start_(prefix), end_(IOAddress::IPV6_ZERO_ADDRESS()), delegated_length_(delegated) {
+ : start_(prefix), end_(IOAddress::IPV6_ZERO_ADDRESS()), prefix_length_(length),
+ delegated_length_(delegated) {
if (!start_.isV6()) {
isc_throw(BadValue, "IPv6 prefix required for prefix delegation range but "
<< start_ << " was specified");
}
- if (delegated_length_ < length) {
+ if (delegated_length_ < prefix_length_) {
isc_throw(BadValue, "delegated length " << static_cast<int>(delegated_length_)
<< " must not be lower than prefix length " << static_cast<int>(length));
}
- if ((length > 128) || (delegated_length_ > 128)) {
+ if ((prefix_length_ > 128) || (delegated_length_ > 128)) {
isc_throw(BadValue, "delegated length " << static_cast<int>(delegated_length_)
<< " and prefix length " << static_cast<int>(length)
<< " must not be greater than 128");
}
- auto prefixes_num = prefixesInRange(length, delegated_length_);
+ auto prefixes_num = prefixesInRange(prefix_length_, delegated_length_);
uint64_t addrs_in_prefix = static_cast<uint64_t>(1) << (128 - delegated_length_);
end_ = offsetAddress(prefix, (prefixes_num - 1) * addrs_in_prefix);
}
PrefixRange::PrefixRange(const asiolink::IOAddress& start, const asiolink::IOAddress& end,
const uint8_t delegated)
- : start_(start), end_(end), delegated_length_(delegated) {
+ : start_(start), end_(end), prefix_length_(0), delegated_length_(delegated) {
if (!start_.isV6() || !end_.isV6()) {
isc_throw(BadValue, "IPv6 prefix required for prefix delegation range but "
<< start_ << ":" << end_ << " was specified");
#include <asiolink/addr_utilities.h>
#include <dhcpsrv/ip_range_permutation.h>
+#include <iostream>
+
using namespace isc::asiolink;
namespace isc {
namespace dhcp {
IPRangePermutation::IPRangePermutation(const IPRangePermutation::Range& range)
- : range_(range), cursor_(addrsInRange(range_.start_, range_.end_) - 1),
+ : range_start_(range.start_), step_(1), cursor_(addrsInRange(range_start_, range.end_) - 1),
state_(), done_(false), generator_() {
std::random_device rd;
generator_.seed(rd());
}
+IPRangePermutation::IPRangePermutation(const IPRangePermutation::PrefixRange& range)
+ : range_start_(range.start_), step_(static_cast<uint64_t>(1) << (128 - range.delegated_length_)),
+ cursor_(prefixesInRange(range.prefix_length_, range.delegated_length_) - 1),
+ state_(), done_(false), generator_() {
+}
+
IOAddress
IPRangePermutation::next(bool& done) {
// If we're done iterating over the pool let's return zero address and
// set the user supplied done flag to true.
if (done_) {
done = true;
- return (range_.start_.isV4() ? IOAddress::IPV4_ZERO_ADDRESS() : IOAddress::IPV6_ZERO_ADDRESS());
+ return (range_start_.isV4() ? IOAddress::IPV4_ZERO_ADDRESS() : IOAddress::IPV6_ZERO_ADDRESS());
}
// If there is one address left, return this address.
// if the range is 192.0.2.1-192.0.2.10 and the picked random position is
// 5, the address we get is 192.0.2.6. This random address will be later
// returned to the caller.
- next_loc_address = offsetAddress(range_.start_, next_loc);
+ next_loc_address = offsetAddress(range_start_, next_loc * step_);
}
// Let's get the address at cursor position in the same way.
if (cursor_existing != state_.end()) {
cursor_address = cursor_existing->second;
} else {
- cursor_address = offsetAddress(range_.start_, cursor_);
+ cursor_address = offsetAddress(range_start_, cursor_ * step_);
}
// Now we swap them.... in fact we don't swap because as an optimization
/// @brief Random IP address/prefix permutation based on Fisher-Yates shuffle.
///
-/// This class is used to shuffle IP addresses within the specified address
-/// range. It is following the Fisher-Yates shuffle algorithm described in
-/// https://en.wikipedia.org/wiki/Fisher–Yates_shuffle.
+/// This class is used to shuffle IP addresses or delegated prefixes within
+/// the specified range. It is following the Fisher-Yates shuffle algorithm
+/// described in https://en.wikipedia.org/wiki/Fisher–Yates_shuffle.
///
/// The original algorithm is modified to keep the minimal information about
/// the current state of the permutation and relies on the caller to collect
/// and store the next available value. In other words, the generated and
/// already returned random values are not stored by this class.
///
-/// The class assumes that initially the IP addresses in the specified range
-/// are in increasing order. Suppose we're dealing with the following address
-/// range: 192.0.2.1-192.0.2.5. Therefore our addresses are initially ordered
-/// like this: a[0]=192.0.2.1, a[1]=192.0.2.2 ..., a[4]=192.0.2.5. The
-/// algorithm starts from the end of that range, i.e. i=4, so a[i]=192.0.2.5.
-/// A random value from the range of [0..i-1] is picked, i.e. a value from the
-/// range of [0..3]. Let's say it is 1. This value initially corresponds to the
-/// address a[1]=192.0.2.2. In the original algorithm the value of a[1] is
-/// swapped with a[4], yelding the following partial permutation:
+/// The class assumes that initially the IP addresses or delegated prefixes
+/// in the specified range are in increasing order. Suppose we're dealing with
+/// the following address range: 192.0.2.1-192.0.2.5. Therefore our addresses
+/// are initially ordered like this: a[0]=192.0.2.1, a[1]=192.0.2.2 ...,
+/// a[4]=192.0.2.5. The algorithm starts from the end of that range, i.e. i=4,
+/// so a[i]=192.0.2.5. A random value from the range of [0..i-1] is picked,
+/// i.e. a value from the range of [0..3]. Let's say it is 1. This value initially
+/// corresponds to the address a[1]=192.0.2.2. In the original algorithm the
+/// value of a[1] is swapped with a[4], yelding the following partial permutation:
/// 192.0.2.1, 192.0.2.5, 192.0.2.3, 192.0.2.4, 192.0.2.2. In our case, we simply
/// return the value of 192.0.2.2 to the caller and remember that
/// a[1]=192.0.2.5. At this point we don't store the values of a[0], a[2] and
/// start and the position. The other two have been already returned to the
/// caller so we forget them.
///
-/// This algorithm guarantees that all IP addresses beloging to the given
-/// address range are returned and no duplicates are returned. The addresses
-/// are returned in a random order.
+/// This algorithm guarantees that all IP addresses or delegated prefixes
+/// beloging to the given range are returned and no duplicates are returned.
+/// The addresses or delegated prefixes are returned in a random order.
class IPRangePermutation {
public:
/// Address range.
typedef AddressRange Range;
- /// @brief Constructor.
+ /// Prefix range.
+ typedef PrefixRange PrefixRange;
+
+ /// @brief Constructor for address ranges.
///
/// @param range address range for which the permutation will be generated.
IPRangePermutation(const Range& range);
- /// @brief Checks if the address range has been exhausted.
+ /// @brief Constructor for prefix ranges.
+ ///
+ /// @param range range of delegated prefixes for which the permutation will
+ /// be generated.
+ IPRangePermutation(const PrefixRange& range);
+
+ /// @brief Checks if the range has been exhausted.
///
- /// @return false if the algorithm went over all addresses in the
- /// range, true otherwise.
+ /// @return false if the algorithm went over all addresses or prefixes in
+ /// the range, true otherwise.
bool exhausted() const {
return (done_);
}
- /// @brief Returns next random address from the permutation.
+ /// @brief Returns next random address or prefix from the permutation.
///
- /// This method will returns all addresses belonging to the specified
- /// address range in random order. For the first number of calls equal
- /// to the size of the address range it guarantees to return a non-zero
- /// IP address from that range without duplicates.
+ /// This method returns all addresses or prefixes belonging to the specified
+ /// range in random order. For the first number of calls equal to the size of
+ /// the range it guarantees to return a non-zero IP address from that range
+ /// without duplicates.
///
/// @param [out] done this parameter is set to true if no more addresses
- /// can be returned for this permutation.
- /// @return next available IP address. It returns IPv4 zero or IPv6 zero
- /// address after this method walked over all available IP addresses in
- /// the range.
+ /// or prefixes can be returned for this permutation.
+ /// @return next available IP address or prefix. It returns IPv4 zero or IPv6
+ /// zero address after this method walked over all available IP addresses or
+ /// prefixes in the range.
asiolink::IOAddress next(bool& done);
private:
- /// Address range used in this permutation and specified in the
- /// constructor.
- Range range_;
+ /// Beginning of the range.
+ asiolink::IOAddress range_start_;
+
+ /// Distance between two neighboring addresses or delegated prefixes,
+ /// i.e. 1 for address range and delegated prefix size for delegated
+ /// prefixes.
+ uint64_t step_;
- /// Keeps the possition of the next address to be swapped with a
- /// randomly picked address from the range of 0..cursor-1. The
- /// cursor value is decreased every time a new IP address is returned.
+ /// Keeps the position of the next address or prefix to be swapped with
+ /// a randomly picked address or prefix from the range of 0..cursor-1. The
+ /// cursor value is decreased every time a new IP address or prefix
+ /// is returned.
uint64_t cursor_;
/// Keeps the current permutation state. The state associates the
- /// swapped IP addresses with their positions in the permutation.
+ /// swapped IP addresses or delegated prefixes with their positions in
+ /// the permutation.
std::map<uint64_t, asiolink::IOAddress> state_;
- /// Indicates if the addresses are exhausted.
+ /// Indicates if the addresses or delegated prefixes are exhausted.
bool done_;
/// Random generator.
} // end of namespace isc::dhcp
} // end of namespace isc
-#endif // ADDRESS_RANGE_PERMUTATION_H
+#endif // IP_RANGE_PERMUTATION_H
EXPECT_TRUE(addrs.begin()->isV4Zero());
}
-// This test verifies that a permutation of IPv4 address range can
+// This test verifies that a permutation of IPv6 address range can
// be generated.
TEST(IPRangePermutationTest, ipv6) {
IPRangePermutation::Range range(IOAddress("2001:db8:1::1:fea0"),
- IOAddress("2001:db8:1::2:abcd"));
+ IOAddress("2001:db8:1::2:abcd"));
IPRangePermutation perm(range);
std::set<IOAddress> addrs;
EXPECT_TRUE(addrs.begin()->isV6Zero());
}
+// This test verifies that a permutation of delegated prefixes can be
+// generated.
+TEST(IPRangePermutationTest, pd) {
+ IPRangePermutation::PrefixRange range(IOAddress("3000::"), 112, 120);
+ IPRangePermutation perm(range);
+
+ std::set<IOAddress> addrs;
+ bool done = false;
+ for (auto i = 0; i < 257; ++i) {
+ auto next = perm.next(done);
+ if (!next.isV6Zero()) {
+ // The IPv6 zero address marks the end of the permutation. In this case
+ // the done flag should be set.
+ EXPECT_LE(range.start_, next);
+ EXPECT_LE(next, range.end_);
+ } else {
+ EXPECT_TRUE(done);
+ EXPECT_TRUE(perm.exhausted());
+ }
+ // Insert the address returned to the set.
+ addrs.insert(next);
+ }
+
+ // We should have recorded 257 unique addresses, including the zero address.
+ EXPECT_EQ(257, addrs.size());
+ EXPECT_TRUE(addrs.begin()->isV6Zero());
+}
+
} // end of anonymous namespace