summaryrefslogtreecommitdiffstats
path: root/src/lib/dhcpsrv/pool.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/dhcpsrv/pool.cc')
-rw-r--r--src/lib/dhcpsrv/pool.cc424
1 files changed, 424 insertions, 0 deletions
diff --git a/src/lib/dhcpsrv/pool.cc b/src/lib/dhcpsrv/pool.cc
new file mode 100644
index 0000000..518b79f
--- /dev/null
+++ b/src/lib/dhcpsrv/pool.cc
@@ -0,0 +1,424 @@
+// Copyright (C) 2012-2020 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
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <asiolink/io_address.h>
+#include <asiolink/addr_utilities.h>
+#include <dhcpsrv/pool.h>
+#include <boost/make_shared.hpp>
+#include <sstream>
+
+using namespace isc::asiolink;
+using namespace isc::data;
+
+namespace isc {
+namespace dhcp {
+
+Pool::Pool(Lease::Type type, const isc::asiolink::IOAddress& first,
+ const isc::asiolink::IOAddress& last)
+ :id_(getNextID()), first_(first), last_(last), type_(type),
+ capacity_(0), cfg_option_(new CfgOption()), client_class_(""),
+ last_allocated_(first), last_allocated_valid_(false),
+ permutation_() {
+}
+
+bool Pool::inRange(const isc::asiolink::IOAddress& addr) const {
+ return (first_ <= addr && addr <= last_);
+}
+
+bool Pool::clientSupported(const ClientClasses& classes) const {
+ return (client_class_.empty() || classes.contains(client_class_));
+}
+
+void Pool::allowClientClass(const ClientClass& class_name) {
+ client_class_ = class_name;
+}
+
+std::string
+Pool::toText() const {
+ std::stringstream tmp;
+ tmp << "type=" << Lease::typeToText(type_) << ", " << first_
+ << "-" << last_;
+ return (tmp.str());
+}
+
+Pool4::Pool4(const isc::asiolink::IOAddress& first,
+ const isc::asiolink::IOAddress& last)
+:Pool(Lease::TYPE_V4, first, last) {
+ // check if specified address boundaries are sane
+ if (!first.isV4() || !last.isV4()) {
+ isc_throw(BadValue, "Invalid Pool4 address boundaries: not IPv4");
+ }
+
+ if (last < first) {
+ isc_throw(BadValue, "Upper boundary is smaller than lower boundary.");
+ }
+
+ // This is IPv4 pool, which only has one type. We can calculate
+ // the number of theoretically possible leases in it. As there's 2^32
+ // possible IPv4 addresses, we'll be able to accurately store that
+ // info.
+ capacity_ = addrsInRange(first, last);
+}
+
+Pool4::Pool4( const isc::asiolink::IOAddress& prefix, uint8_t prefix_len)
+:Pool(Lease::TYPE_V4, prefix, IOAddress("0.0.0.0")) {
+
+ // check if the prefix is sane
+ if (!prefix.isV4()) {
+ isc_throw(BadValue, "Invalid Pool4 address boundaries: not IPv4");
+ }
+
+ // check if the prefix length is sane
+ if (prefix_len == 0 || prefix_len > 32) {
+ isc_throw(BadValue, "Invalid prefix length");
+ }
+
+ // Let's now calculate the last address in defined pool
+ last_ = lastAddrInPrefix(prefix, prefix_len);
+
+ // This is IPv4 pool, which only has one type. We can calculate
+ // the number of theoretically possible leases in it. As there's 2^32
+ // possible IPv4 addresses, we'll be able to accurately store that
+ // info.
+ capacity_ = addrsInRange(prefix, last_);
+}
+
+Pool4Ptr
+Pool4::create(const IOAddress& first, const IOAddress& last) {
+ return (boost::make_shared<Pool4>(first, last));
+}
+
+Pool4Ptr
+Pool4::create(const IOAddress& prefix, uint8_t prefix_len) {
+ return (boost::make_shared<Pool4>(prefix, prefix_len));
+}
+
+data::ElementPtr
+Pool::toElement() const {
+ // Prepare the map
+ ElementPtr map = Element::createMap();
+
+ // Set user-context
+ contextToElement(map);
+
+ // Set pool options
+ ConstCfgOptionPtr opts = getCfgOption();
+ map->set("option-data", opts->toElement());
+
+ // Set client-class
+ const ClientClass& cclass = getClientClass();
+ if (!cclass.empty()) {
+ map->set("client-class", Element::create(cclass));
+ }
+
+ // Set require-client-classes
+ const ClientClasses& classes = getRequiredClasses();
+ if (!classes.empty()) {
+ ElementPtr class_list =Element::createList();
+ for (ClientClasses::const_iterator it = classes.cbegin();
+ it != classes.cend(); ++it) {
+ class_list->add(Element::create(*it));
+ }
+ map->set("require-client-classes", class_list);
+ }
+
+ return (map);
+}
+
+data::ElementPtr
+Pool4::toElement() const {
+ // Prepare the map
+ ElementPtr map = Pool::toElement();
+
+ // Set pool
+ const IOAddress& first = getFirstAddress();
+ const IOAddress& last = getLastAddress();
+ std::string range = first.toText() + "-" + last.toText();
+
+ // Try to output a prefix (vs a range)
+ int prefix_len = prefixLengthFromRange(first, last);
+ if (prefix_len >= 0) {
+ std::ostringstream oss;
+ oss << first.toText() << "/" << prefix_len;
+ range = oss.str();
+ }
+
+ map->set("pool", Element::create(range));
+ return (map);
+}
+
+
+Pool6::Pool6(Lease::Type type, const isc::asiolink::IOAddress& first,
+ const isc::asiolink::IOAddress& last)
+ : Pool(type, first, last), prefix_len_(128), pd_exclude_option_() {
+
+ // check if specified address boundaries are sane
+ if (!first.isV6() || !last.isV6()) {
+ isc_throw(BadValue, "Invalid Pool6 address boundaries: not IPv6");
+ }
+
+ if ( (type != Lease::TYPE_NA) && (type != Lease::TYPE_TA) &&
+ (type != Lease::TYPE_PD)) {
+ isc_throw(BadValue, "Invalid Pool6 type: " << static_cast<int>(type)
+ << ", must be TYPE_IA, TYPE_TA or TYPE_PD");
+ }
+
+ if (last < first) {
+ isc_throw(BadValue, "Upper boundary is smaller than lower boundary.");
+ // This check is a bit strict. If we decide that it is too strict,
+ // we need to comment it and uncomment lines below.
+ // On one hand, letting the user specify 2001::f - 2001::1 is nice, but
+ // on the other hand, 2001::1 may be a typo and the user really meant
+ // 2001::1:0 (or 1 followed by some hex digit), so a at least a warning
+ // would be useful.
+
+ // first_ = last;
+ // last_ = first;
+ }
+
+ // TYPE_PD is not supported by this constructor. first-last style
+ // parameters are for IA and TA only. There is another dedicated
+ // constructor for that (it uses prefix/length)
+ if ((type != Lease::TYPE_NA) && (type != Lease::TYPE_TA)) {
+ isc_throw(BadValue, "Invalid Pool6 type specified: "
+ << static_cast<int>(type));
+ }
+
+ // Let's calculate the theoretical number of leases in this pool.
+ // If the pool is extremely large (i.e. contains more than 2^64 addresses,
+ // we'll just cap it at max value of uint64_t).
+ capacity_ = addrsInRange(first, last);
+}
+
+Pool6::Pool6(Lease::Type type, const isc::asiolink::IOAddress& prefix,
+ const uint8_t prefix_len, const uint8_t delegated_len /* = 128 */)
+ : Pool(type, prefix, IOAddress::IPV6_ZERO_ADDRESS()),
+ prefix_len_(delegated_len), pd_exclude_option_() {
+
+ init(type, prefix, prefix_len, delegated_len,
+ IOAddress::IPV6_ZERO_ADDRESS(), 0);
+}
+
+Pool6::Pool6(const asiolink::IOAddress& prefix, const uint8_t prefix_len,
+ const uint8_t delegated_len,
+ const asiolink::IOAddress& excluded_prefix,
+ const uint8_t excluded_prefix_len)
+ : Pool(Lease::TYPE_PD, prefix, IOAddress::IPV6_ZERO_ADDRESS()),
+ prefix_len_(delegated_len), pd_exclude_option_() {
+
+ init(Lease::TYPE_PD, prefix, prefix_len, delegated_len, excluded_prefix,
+ excluded_prefix_len);
+
+ // The excluded prefix can only be specified using this constructor.
+ // Therefore, the initialization of the excluded prefix is takes place
+ // here, rather than in the init(...) function.
+ if (!excluded_prefix.isV6()) {
+ isc_throw(BadValue, "excluded prefix must be an IPv6 prefix");
+ }
+
+ // An "unspecified" prefix should have both value and length equal to 0.
+ if ((excluded_prefix.isV6Zero() && (excluded_prefix_len != 0)) ||
+ (!excluded_prefix.isV6Zero() && (excluded_prefix_len == 0))) {
+ isc_throw(BadValue, "invalid excluded prefix "
+ << excluded_prefix << "/"
+ << static_cast<unsigned>(excluded_prefix_len));
+ }
+
+ // If excluded prefix has been specified.
+ if (!excluded_prefix.isV6Zero() && (excluded_prefix_len != 0)) {
+
+ // Excluded prefix length must not be greater than 128.
+ if (excluded_prefix_len > 128) {
+ isc_throw(BadValue, "excluded prefix length "
+ << static_cast<unsigned>(excluded_prefix_len)
+ << " must not be greater than 128");
+ }
+
+ // Excluded prefix must be a sub-prefix of a delegated prefix. First
+ // check the prefix length as it is less involved.
+ if (excluded_prefix_len <= prefix_len_) {
+ isc_throw(BadValue, "excluded prefix length "
+ << static_cast<unsigned>(excluded_prefix_len)
+ << " must be lower than the delegated prefix length "
+ << static_cast<unsigned>(prefix_len_));
+ }
+
+ /// @todo Check that the prefixes actually match. Theoretically, a
+ /// user could specify a prefix which sets insignificant bits. We should
+ /// clear insignificant bits based on the prefix length but this
+ /// should be considered a part of the IOAddress class, perhaps and
+ /// requires a bit of work (mainly in terms of testing).
+ }
+}
+
+Pool6Ptr
+Pool6::create(Lease::Type type, const IOAddress& first, const IOAddress& last) {
+ return (boost::make_shared<Pool6>(type, first, last));
+}
+
+Pool6Ptr
+Pool6::create(Lease::Type type, const IOAddress& prefix,
+ uint8_t prefix_len, uint8_t delegated_len) {
+ return (boost::make_shared<Pool6>(type, prefix, prefix_len, delegated_len));
+}
+
+Pool6Ptr
+Pool6::create(const IOAddress& prefix, const uint8_t prefix_len,
+ const uint8_t delegated_len, const IOAddress& excluded_prefix,
+ const uint8_t excluded_prefix_len) {
+ return (boost::make_shared<Pool6>(prefix, prefix_len,
+ delegated_len, excluded_prefix,
+ excluded_prefix_len));
+}
+
+void
+Pool6::init(const Lease::Type& type,
+ const asiolink::IOAddress& prefix,
+ const uint8_t prefix_len,
+ const uint8_t delegated_len,
+ const asiolink::IOAddress& excluded_prefix,
+ const uint8_t excluded_prefix_len) {
+ // Check if the prefix is sane
+ if (!prefix.isV6()) {
+ isc_throw(BadValue, "Invalid Pool6 address boundaries: not IPv6");
+ }
+
+ // Check if the prefix length is sane
+ if (prefix_len == 0 || prefix_len > 128) {
+ isc_throw(BadValue, "Invalid prefix length: "
+ << static_cast<unsigned>(prefix_len));
+ }
+
+ if (prefix_len > delegated_len) {
+ isc_throw(BadValue, "Delegated length ("
+ << static_cast<int>(delegated_len)
+ << ") must be longer than or equal to prefix length ("
+ << static_cast<int>(prefix_len) << ")");
+ }
+
+ if ( ( (type == Lease::TYPE_NA) || (type == Lease::TYPE_TA)) &&
+ (delegated_len != 128)) {
+ isc_throw(BadValue, "For IA or TA pools, delegated prefix length must"
+ << " be 128.");
+ }
+
+ // excluded_prefix_len == 0 means there's no excluded prefix at all.
+ if (excluded_prefix_len && (excluded_prefix_len < delegated_len)) {
+ isc_throw(BadValue, "Excluded prefix ("
+ << static_cast<int>(excluded_prefix_len)
+ << ") must be longer than or equal to the delegated prefix length ("
+ << static_cast<int>(delegated_len) << ")");
+ }
+
+ /// @todo: We should probably implement checks against weird addresses
+ /// here, like ::, starting with fe80, starting with ff etc. .
+
+ // Let's now calculate the last address in defined pool
+ last_ = lastAddrInPrefix(prefix, prefix_len);
+
+ // Let's calculate the theoretical number of leases in this pool.
+ // For addresses, we could use addrsInRange(prefix, last_), but it's
+ // much faster to do calculations on prefix lengths.
+ capacity_ = prefixesInRange(prefix_len, delegated_len);
+
+ // If user specified an excluded prefix, create an option that will
+ // be sent to clients obtaining prefixes from this pool.
+ if (excluded_prefix_len > 0) {
+ pd_exclude_option_.reset(new Option6PDExclude(prefix, delegated_len,
+ excluded_prefix,
+ excluded_prefix_len));
+ }
+}
+
+data::ElementPtr
+Pool6::toElement() const {
+ // Prepare the map
+ ElementPtr map = Pool::toElement();
+
+ switch (getType()) {
+ case Lease::TYPE_NA: {
+ const IOAddress& first = getFirstAddress();
+ const IOAddress& last = getLastAddress();
+ std::string range = first.toText() + "-" + last.toText();
+
+ // Try to output a prefix (vs a range)
+ int prefix_len = prefixLengthFromRange(first, last);
+ if (prefix_len >= 0) {
+ std::ostringstream oss;
+ oss << first.toText() << "/" << prefix_len;
+ range = oss.str();
+ }
+
+ map->set("pool", Element::create(range));
+ break;
+ }
+ case Lease::TYPE_PD: {
+ // Set prefix
+ const IOAddress& prefix = getFirstAddress();
+ map->set("prefix", Element::create(prefix.toText()));
+
+ // Set prefix-len (get it from min - max)
+ const IOAddress& last = getLastAddress();
+ int prefix_len = prefixLengthFromRange(prefix, last);
+ if (prefix_len < 0) {
+ // The pool is bad: give up
+ isc_throw(ToElementError, "invalid prefix range "
+ << prefix.toText() << "-" << last.toText());
+ }
+ map->set("prefix-len", Element::create(prefix_len));
+
+ // Set delegated-len
+ uint8_t len = getLength();
+ map->set("delegated-len", Element::create(static_cast<int>(len)));
+
+ // Set excluded prefix
+ const Option6PDExcludePtr& xopt = getPrefixExcludeOption();
+ if (xopt) {
+ const IOAddress& xprefix = xopt->getExcludedPrefix(prefix, len);
+ map->set("excluded-prefix", Element::create(xprefix.toText()));
+
+ uint8_t xlen = xopt->getExcludedPrefixLength();
+ map->set("excluded-prefix-len",
+ Element::create(static_cast<int>(xlen)));
+ }
+ // Let's not insert empty excluded-prefix values. If we ever
+ // decide to insert it after all, here's the code to do it:
+ // else {
+ // map->set("excluded-prefix",
+ // Element::create(std::string("::")));
+ // map->set("excluded-prefix-len", Element::create(0));
+ /// }
+
+ break;
+ }
+ default:
+ isc_throw(ToElementError, "Lease type: " << getType()
+ << ", unsupported for Pool6");
+ break;
+ }
+
+ return (map);
+}
+
+
+std::string
+Pool6::toText() const {
+ std::ostringstream s;
+ s << "type=" << Lease::typeToText(type_) << ", " << first_
+ << "-" << last_ << ", delegated_len="
+ << static_cast<unsigned>(prefix_len_);
+
+ if (pd_exclude_option_) {
+ s << ", excluded_prefix_len="
+ << static_cast<unsigned>(pd_exclude_option_->getExcludedPrefixLength());
+ }
+ return (s.str());
+}
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace