Kea  2.3.5-git
pool.cc
Go to the documentation of this file.
1 // Copyright (C) 2012-2023 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 
7 #include <config.h>
8 
9 #include <asiolink/io_address.h>
11 #include <dhcpsrv/pool.h>
12 #include <boost/make_shared.hpp>
13 #include <sstream>
14 
15 using namespace isc::asiolink;
16 using namespace isc::data;
17 
18 namespace isc {
19 namespace dhcp {
20 
21 Pool::Pool(Lease::Type type, const isc::asiolink::IOAddress& first,
22  const isc::asiolink::IOAddress& last)
23  : id_(getNextID()), first_(first), last_(last), type_(type),
24  capacity_(0), cfg_option_(new CfgOption()), client_class_(""),
25  permutation_() {
26 }
27 
28 bool Pool::inRange(const isc::asiolink::IOAddress& addr) const {
29  return (first_ <= addr && addr <= last_);
30 }
31 
32 bool Pool::clientSupported(const ClientClasses& classes) const {
33  return (client_class_.empty() || classes.contains(client_class_));
34 }
35 
36 void Pool::allowClientClass(const ClientClass& class_name) {
37  client_class_ = class_name;
38 }
39 
40 std::string
41 Pool::toText() const {
42  std::stringstream tmp;
43  tmp << "type=" << Lease::typeToText(type_) << ", " << first_
44  << "-" << last_;
45  return (tmp.str());
46 }
47 
49  const isc::asiolink::IOAddress& last)
50  : Pool(Lease::TYPE_V4, first, last) {
51  // check if specified address boundaries are sane
52  if (!first.isV4() || !last.isV4()) {
53  isc_throw(BadValue, "Invalid Pool4 address boundaries: not IPv4");
54  }
55 
56  if (last < first) {
57  isc_throw(BadValue, "Upper boundary is smaller than lower boundary.");
58  }
59 
60  // This is IPv4 pool, which only has one type. We can calculate
61  // the number of theoretically possible leases in it. As there's 2^32
62  // possible IPv4 addresses, we'll be able to accurately store that
63  // info.
64  capacity_ = addrsInRange(first, last);
65 }
66 
67 Pool4::Pool4(const isc::asiolink::IOAddress& prefix, uint8_t prefix_len)
68  : Pool(Lease::TYPE_V4, prefix, IOAddress("0.0.0.0")) {
69 
70  // check if the prefix is sane
71  if (!prefix.isV4()) {
72  isc_throw(BadValue, "Invalid Pool4 address boundaries: not IPv4");
73  }
74 
75  // check if the prefix length is sane
76  if (prefix_len == 0 || prefix_len > 32) {
77  isc_throw(BadValue, "Invalid prefix length");
78  }
79 
80  IOAddress first_address = firstAddrInPrefix(prefix, prefix_len);
81  if (first_address != prefix) {
82  isc_throw(BadValue, "Invalid Pool4 address boundaries: " << prefix
83  << " is not the first address in prefix: " << first_address
84  << "/" << static_cast<uint32_t>(prefix_len));
85  }
86 
87  // Let's now calculate the last address in defined pool
88  last_ = lastAddrInPrefix(prefix, prefix_len);
89 
90  // This is IPv4 pool, which only has one type. We can calculate
91  // the number of theoretically possible leases in it. As there's 2^32
92  // possible IPv4 addresses, we'll be able to accurately store that
93  // info.
94  capacity_ = addrsInRange(prefix, last_);
95 }
96 
98 Pool4::create(const IOAddress& first, const IOAddress& last) {
99  return (boost::make_shared<Pool4>(first, last));
100 }
101 
102 Pool4Ptr
103 Pool4::create(const IOAddress& prefix, uint8_t prefix_len) {
104  return (boost::make_shared<Pool4>(prefix, prefix_len));
105 }
106 
109  // Prepare the map
111 
112  // Set user-context
113  contextToElement(map);
114 
115  // Set pool options
117  map->set("option-data", opts->toElement());
118 
119  // Set client-class
120  const ClientClass& cclass = getClientClass();
121  if (!cclass.empty()) {
122  map->set("client-class", Element::create(cclass));
123  }
124 
125  // Set require-client-classes
126  const ClientClasses& classes = getRequiredClasses();
127  if (!classes.empty()) {
128  ElementPtr class_list =Element::createList();
129  for (ClientClasses::const_iterator it = classes.cbegin();
130  it != classes.cend(); ++it) {
131  class_list->add(Element::create(*it));
132  }
133  map->set("require-client-classes", class_list);
134  }
135 
136  return (map);
137 }
138 
141  // Prepare the map
142  ElementPtr map = Pool::toElement();
143 
144  // Set pool
145  const IOAddress& first = getFirstAddress();
146  const IOAddress& last = getLastAddress();
147  std::string range = first.toText() + "-" + last.toText();
148 
149  // Try to output a prefix (vs a range)
150  int prefix_len = prefixLengthFromRange(first, last);
151  if (prefix_len >= 0) {
152  std::ostringstream oss;
153  oss << first.toText() << "/" << prefix_len;
154  range = oss.str();
155  }
156 
157  map->set("pool", Element::create(range));
158  return (map);
159 }
160 
162  const isc::asiolink::IOAddress& last)
163  : Pool(type, first, last), prefix_len_(128), pd_exclude_option_() {
164 
165  // check if specified address boundaries are sane
166  if (!first.isV6() || !last.isV6()) {
167  isc_throw(BadValue, "Invalid Pool6 address boundaries: not IPv6");
168  }
169 
170  if ((type != Lease::TYPE_NA) && (type != Lease::TYPE_TA) &&
171  (type != Lease::TYPE_PD)) {
172  isc_throw(BadValue, "Invalid Pool6 type: " << static_cast<int>(type)
173  << ", must be TYPE_IA, TYPE_TA or TYPE_PD");
174  }
175 
176  if (last < first) {
177  isc_throw(BadValue, "Upper boundary is smaller than lower boundary.");
178  // This check is a bit strict. If we decide that it is too strict,
179  // we need to comment it and uncomment lines below.
180  // On one hand, letting the user specify 2001::f - 2001::1 is nice, but
181  // on the other hand, 2001::1 may be a typo and the user really meant
182  // 2001::1:0 (or 1 followed by some hex digit), so a at least a warning
183  // would be useful.
184 
185  // first_ = last;
186  // last_ = first;
187  }
188 
189  // TYPE_PD is not supported by this constructor. first-last style
190  // parameters are for IA and TA only. There is another dedicated
191  // constructor for that (it uses prefix/length)
192  if ((type != Lease::TYPE_NA) && (type != Lease::TYPE_TA)) {
193  isc_throw(BadValue, "Invalid Pool6 type specified: "
194  << static_cast<int>(type));
195  }
196 
197  // Let's calculate the theoretical number of leases in this pool.
198  // If the pool is extremely large (i.e. contains more than 2^64 addresses,
199  // we'll just cap it at max value of uint64_t).
200  capacity_ = addrsInRange(first, last);
201 }
202 
204  const uint8_t prefix_len, const uint8_t delegated_len /* = 128 */)
205  : Pool(type, prefix, IOAddress::IPV6_ZERO_ADDRESS()),
206  prefix_len_(delegated_len), pd_exclude_option_() {
207 
208  init(type, prefix, prefix_len, delegated_len,
210 }
211 
212 Pool6::Pool6(const asiolink::IOAddress& prefix, const uint8_t prefix_len,
213  const uint8_t delegated_len,
214  const asiolink::IOAddress& excluded_prefix,
215  const uint8_t excluded_prefix_len)
216  : Pool(Lease::TYPE_PD, prefix, IOAddress::IPV6_ZERO_ADDRESS()),
217  prefix_len_(delegated_len), pd_exclude_option_() {
218 
219  init(Lease::TYPE_PD, prefix, prefix_len, delegated_len, excluded_prefix,
220  excluded_prefix_len);
221 
222  // The excluded prefix can only be specified using this constructor.
223  // Therefore, the initialization of the excluded prefix is takes place
224  // here, rather than in the init(...) function.
225  if (!excluded_prefix.isV6()) {
226  isc_throw(BadValue, "excluded prefix must be an IPv6 prefix");
227  }
228 
229  // An "unspecified" prefix should have both value and length equal to 0.
230  if ((excluded_prefix.isV6Zero() && (excluded_prefix_len != 0)) ||
231  (!excluded_prefix.isV6Zero() && (excluded_prefix_len == 0))) {
232  isc_throw(BadValue, "invalid excluded prefix "
233  << excluded_prefix << "/"
234  << static_cast<unsigned>(excluded_prefix_len));
235  }
236 
237  // If excluded prefix has been specified.
238  if (!excluded_prefix.isV6Zero() && (excluded_prefix_len != 0)) {
239  // Excluded prefix length must not be greater than 128.
240  if (excluded_prefix_len > 128) {
241  isc_throw(BadValue, "excluded prefix length "
242  << static_cast<unsigned>(excluded_prefix_len)
243  << " must not be greater than 128");
244  }
245 
246  // Excluded prefix must be a sub-prefix of a delegated prefix. First
247  // check the prefix length as it is less involved.
248  if (excluded_prefix_len <= prefix_len_) {
249  isc_throw(BadValue, "excluded prefix length "
250  << static_cast<unsigned>(excluded_prefix_len)
251  << " must be longer than the delegated prefix length "
252  << static_cast<unsigned>(prefix_len_));
253  }
254 
260  }
261 }
262 
263 Pool6Ptr
264 Pool6::create(Lease::Type type, const IOAddress& first, const IOAddress& last) {
265  return (boost::make_shared<Pool6>(type, first, last));
266 }
267 
268 Pool6Ptr
269 Pool6::create(Lease::Type type, const IOAddress& prefix,
270  uint8_t prefix_len, uint8_t delegated_len) {
271  return (boost::make_shared<Pool6>(type, prefix, prefix_len, delegated_len));
272 }
273 
274 Pool6Ptr
275 Pool6::create(const IOAddress& prefix, const uint8_t prefix_len,
276  const uint8_t delegated_len, const IOAddress& excluded_prefix,
277  const uint8_t excluded_prefix_len) {
278  return (boost::make_shared<Pool6>(prefix, prefix_len,
279  delegated_len, excluded_prefix,
280  excluded_prefix_len));
281 }
282 
283 void
284 Pool6::init(const Lease::Type& type,
285  const asiolink::IOAddress& prefix,
286  const uint8_t prefix_len,
287  const uint8_t delegated_len,
288  const asiolink::IOAddress& excluded_prefix,
289  const uint8_t excluded_prefix_len) {
290  // Check if the prefix is sane
291  if (!prefix.isV6()) {
292  isc_throw(BadValue, "Invalid Pool6 address boundaries: not IPv6");
293  }
294 
295  // Check if the prefix length is sane
296  if (prefix_len == 0 || prefix_len > 128) {
297  isc_throw(BadValue, "Invalid prefix length: "
298  << static_cast<unsigned>(prefix_len));
299  }
300 
301  if (prefix_len > delegated_len) {
302  isc_throw(BadValue, "Delegated length ("
303  << static_cast<int>(delegated_len)
304  << ") must be longer than or equal to prefix length ("
305  << static_cast<int>(prefix_len) << ")");
306  }
307 
308  if (((type == Lease::TYPE_NA) || (type == Lease::TYPE_TA)) &&
309  (delegated_len != 128)) {
310  isc_throw(BadValue, "For IA or TA pools, delegated prefix length must"
311  << " be 128.");
312  }
313 
314  // excluded_prefix_len == 0 means there's no excluded prefix at all.
315  if (excluded_prefix_len && (excluded_prefix_len <= delegated_len)) {
316  isc_throw(BadValue, "Excluded prefix ("
317  << static_cast<int>(excluded_prefix_len)
318  << ") must be longer than the delegated prefix length ("
319  << static_cast<int>(delegated_len) << ")");
320  }
321 
322  IOAddress first_address = firstAddrInPrefix(prefix, prefix_len);
323  if (first_address != prefix) {
324  isc_throw(BadValue, "Invalid Pool6 address boundaries: " << prefix
325  << " is not the first address in prefix: " << first_address
326  << "/" << static_cast<uint32_t>(prefix_len));
327  }
328 
331 
332  // Let's now calculate the last address in defined pool
333  last_ = lastAddrInPrefix(prefix, prefix_len);
334 
335  // Let's calculate the theoretical number of leases in this pool.
336  // For addresses, we could use addrsInRange(prefix, last_), but it's
337  // much faster to do calculations on prefix lengths.
338  capacity_ = prefixesInRange(prefix_len, delegated_len);
339 
340  // If user specified an excluded prefix, create an option that will
341  // be sent to clients obtaining prefixes from this pool.
342  if (excluded_prefix_len > 0) {
343  pd_exclude_option_.reset(new Option6PDExclude(prefix, delegated_len,
344  excluded_prefix,
345  excluded_prefix_len));
346  }
347 }
348 
351  // Prepare the map
352  ElementPtr map = Pool::toElement();
353 
354  switch (getType()) {
355  case Lease::TYPE_NA: {
356  const IOAddress& first = getFirstAddress();
357  const IOAddress& last = getLastAddress();
358  std::string range = first.toText() + "-" + last.toText();
359 
360  // Try to output a prefix (vs a range)
361  int prefix_len = prefixLengthFromRange(first, last);
362  if (prefix_len >= 0) {
363  std::ostringstream oss;
364  oss << first.toText() << "/" << prefix_len;
365  range = oss.str();
366  }
367 
368  map->set("pool", Element::create(range));
369  break;
370  }
371  case Lease::TYPE_PD: {
372  // Set prefix
373  const IOAddress& prefix = getFirstAddress();
374  map->set("prefix", Element::create(prefix.toText()));
375 
376  // Set prefix-len (get it from min - max)
377  const IOAddress& last = getLastAddress();
378  int prefix_len = prefixLengthFromRange(prefix, last);
379  if (prefix_len < 0) {
380  // The pool is bad: give up
381  isc_throw(ToElementError, "invalid prefix range "
382  << prefix.toText() << "-" << last.toText());
383  }
384  map->set("prefix-len", Element::create(prefix_len));
385 
386  // Set delegated-len
387  uint8_t len = getLength();
388  map->set("delegated-len", Element::create(static_cast<int>(len)));
389 
390  // Set excluded prefix
392  if (xopt) {
393  const IOAddress& xprefix = xopt->getExcludedPrefix(prefix, len);
394  map->set("excluded-prefix", Element::create(xprefix.toText()));
395 
396  uint8_t xlen = xopt->getExcludedPrefixLength();
397  map->set("excluded-prefix-len",
398  Element::create(static_cast<int>(xlen)));
399  }
400  // Let's not insert empty excluded-prefix values. If we ever
401  // decide to insert it after all, here's the code to do it:
402  // else {
403  // map->set("excluded-prefix",
404  // Element::create(std::string("::")));
405  // map->set("excluded-prefix-len", Element::create(0));
407 
408  break;
409  }
410  default:
411  isc_throw(ToElementError, "Lease type: " << getType()
412  << ", unsupported for Pool6");
413  break;
414  }
415 
416  return (map);
417 }
418 
419 std::string
420 Pool6::toText() const {
421  std::ostringstream s;
422  s << "type=" << Lease::typeToText(type_) << ", " << first_
423  << "-" << last_ << ", delegated_len="
424  << static_cast<unsigned>(prefix_len_);
425 
426  if (pd_exclude_option_) {
427  s << ", excluded_prefix_len="
428  << static_cast<unsigned>(pd_exclude_option_->getExcludedPrefixLength());
429  }
430  return (s.str());
431 }
432 
433 } // namespace dhcp
434 } // namespace isc
boost::shared_ptr< Pool4 > Pool4Ptr
a pointer an IPv4 Pool
Definition: pool.h:239
const isc::asiolink::IOAddress & getFirstAddress() const
Returns the first address in a pool.
Definition: pool.h:48
const ClientClasses & getRequiredClasses() const
Returns classes which are required to be evaluated.
Definition: pool.h:136
void allowClientClass(const ClientClass &class_name)
Sets the supported class to class class_name.
Definition: pool.cc:36
static Pool4Ptr create(const isc::asiolink::IOAddress &first, const isc::asiolink::IOAddress &last)
Factory function for creating an instance of the Pool4.
Definition: pool.cc:98
boost::shared_ptr< Pool6 > Pool6Ptr
a pointer an IPv6 Pool
Definition: pool.h:298
static std::string typeToText(Type type)
returns text representation of a lease type
Definition: lease.cc:53
bool contains(const ClientClass &x) const
returns if class x belongs to the defined classes
Definition: classify.cc:49
boost::shared_ptr< const CfgOption > ConstCfgOptionPtr
Const pointer.
Definition: cfg_option.h:748
Cannot unparse error.
DHCPv6 option class representing Prefix Exclude Option (RFC 6603).
the lease contains IPv6 prefix (for prefix delegation)
Definition: lease.h:49
boost::shared_ptr< Element > ElementPtr
Definition: data.h:24
static ElementPtr createMap(const Position &pos=ZERO_POSITION())
Creates an empty MapElement type ElementPtr.
Definition: data.cc:291
const_iterator cbegin() const
Iterators to the first element.
Definition: classify.h:152
static ElementPtr createList(const Position &pos=ZERO_POSITION())
Creates an empty ListElement type ElementPtr.
Definition: data.cc:286
Pool6(Lease::Type type, const isc::asiolink::IOAddress &first, const isc::asiolink::IOAddress &last)
the constructor for Pool6 "min-max" style definition
Definition: pool.cc:161
static Pool6Ptr create(Lease::Type type, const isc::asiolink::IOAddress &first, const isc::asiolink::IOAddress &last)
Factory function for creating an instance of the Pool6.
Definition: pool.cc:264
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
ClientClassContainer::const_iterator const_iterator
Type of iterators.
Definition: classify.h:112
Represents option data configuration for the DHCP server.
Definition: cfg_option.h:318
Lease::Type getType() const
returns pool type
Definition: pool.h:424
Option6PDExcludePtr getPrefixExcludeOption() const
Returns instance of the pool specific Prefix Exclude option.
Definition: pool.h:441
Lease::Type type_
defines a lease type that will be served from this pool
Definition: pool.h:203
bool clientSupported(const ClientClasses &client_classes) const
Checks whether this pool supports client that belongs to specified classes.
Definition: pool.cc:32
void contextToElement(data::ElementPtr map) const
Merge unparse a user_context object.
Definition: user_context.cc:15
ClientClass client_class_
Optional definition of a client class.
Definition: pool.h:219
the lease contains temporary IPv6 address
Definition: lease.h:48
uint8_t getLength() const
returns delegated prefix length
Definition: pool.h:433
bool empty() const
Check if classes is empty.
Definition: classify.h:138
the lease contains non-temporary IPv6 address
Definition: lease.h:47
virtual std::string toText() const
returns textual representation of the pool
Definition: pool.cc:41
isc::asiolink::IOAddress first_
The first address in a pool.
Definition: pool.h:197
boost::shared_ptr< Option6PDExclude > Option6PDExcludePtr
Pointer to the Option6PDExclude object.
virtual data::ElementPtr toElement() const
Unparse a Pool6 object.
Definition: pool.cc:350
Defines the logger used by the top-level component of kea-lfc.
virtual data::ElementPtr toElement() const
Unparse a Pool4 object.
Definition: pool.cc:140
Pool4(const isc::asiolink::IOAddress &first, const isc::asiolink::IOAddress &last)
the constructor for Pool4 "min-max" style definition
Definition: pool.cc:48
isc::asiolink::IOAddress last_
The last address in a pool.
Definition: pool.h:200
const isc::asiolink::IOAddress & getLastAddress() const
Returns the last address in a pool.
Definition: pool.h:54
static ElementPtr create(const Position &pos=ZERO_POSITION())
Definition: data.cc:241
a common structure for IPv4 and IPv6 leases
Definition: lease.h:31
Type
Type of lease or pool.
Definition: lease.h:46
const ClientClass & getClientClass() const
returns the client class
Definition: pool.h:122
CfgOptionPtr getCfgOption()
Returns pointer to the option data configuration for this pool.
Definition: pool.h:92
const_iterator cend() const
Iterators to the past the end element.
Definition: classify.h:165
virtual std::string toText() const
returns textual representation of the pool
Definition: pool.cc:420
base class for Pool4 and Pool6
Definition: pool.h:31
std::string ClientClass
Defines a single class name.
Definition: classify.h:42
virtual data::ElementPtr toElement() const
Unparse a pool object.
Definition: pool.cc:108
Container for storing client class names.
Definition: classify.h:108
uint64_t capacity_
Stores number of possible leases.
Definition: pool.h:211
bool inRange(const isc::asiolink::IOAddress &addr) const
Checks if a given address is in the range.
Definition: pool.cc:28