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