1 // Copyright (C) 2012-2021 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>
10 #include <asiolink/addr_utilities.h>
11 #include <dhcp/option_space.h>
12 #include <dhcpsrv/shared_network.h>
13 #include <dhcpsrv/subnet.h>
14 #include <util/multi_threading_mgr.h>
15 
16 #include <boost/lexical_cast.hpp>
17 #include <boost/make_shared.hpp>
18 
19 #include <algorithm>
20 #include <sstream>
21 
22 using namespace isc::asiolink;
23 using namespace isc::data;
24 using namespace isc::dhcp;
25 using namespace isc::util;
26 
27 namespace {
28 
29 /// @brief Function used in calls to std::upper_bound to check
30 /// if the specified prefix is lower than the first address a pool.
31 ///
32 /// @return true if prefix is lower than the first address in the pool.
33 bool
prefixLessThanFirstAddress(const IOAddress & prefix,const PoolPtr & pool)34 prefixLessThanFirstAddress(const IOAddress& prefix, const PoolPtr& pool) {
35     return (prefix < pool->getFirstAddress());
36 }
37 
38 /// @brief Function used in calls to std::sort to compare first
39 /// prefixes of the two pools.
40 ///
41 /// @param pool1 First pool.
42 /// @param pool2 Second pool.
43 ///
44 /// @return true if first prefix of the first pool is smaller than
45 /// the first address of the second pool.
46 bool
comparePoolFirstAddress(const PoolPtr & pool1,const PoolPtr & pool2)47 comparePoolFirstAddress(const PoolPtr& pool1, const PoolPtr& pool2) {
48     return (pool1->getFirstAddress() < pool2->getFirstAddress());
49 };
50 
51 }
52 
53 namespace isc {
54 namespace dhcp {
55 
56 // This is an initial value of subnet-id. See comments in subnet.h for details.
57 SubnetID Subnet::static_id_ = 1;
58 
Subnet(const isc::asiolink::IOAddress & prefix,uint8_t len,const SubnetID id)59 Subnet::Subnet(const isc::asiolink::IOAddress& prefix, uint8_t len,
60                const SubnetID id)
61     : id_(id == 0 ? generateNextID() : id), prefix_(prefix),
62       prefix_len_(len),
63       last_allocated_ia_(lastAddrInPrefix(prefix, len)),
64       last_allocated_ta_(lastAddrInPrefix(prefix, len)),
65       last_allocated_pd_(lastAddrInPrefix(prefix, len)),
66       last_allocated_time_(),
67       shared_network_name_(),
68       mutex_(new std::mutex) {
69     if ((prefix.isV6() && len > 128) ||
70         (prefix.isV4() && len > 32)) {
71         isc_throw(BadValue,
72                   "Invalid prefix length specified for subnet: " << len);
73     }
74 
75     // Initialize timestamps for each lease type to negative infinity.
76     last_allocated_time_[Lease::TYPE_V4] = boost::posix_time::neg_infin;
77     last_allocated_time_[Lease::TYPE_NA] = boost::posix_time::neg_infin;
78     last_allocated_time_[Lease::TYPE_TA] = boost::posix_time::neg_infin;
79     last_allocated_time_[Lease::TYPE_PD] = boost::posix_time::neg_infin;
80 }
81 
82 bool
inRange(const isc::asiolink::IOAddress & addr) const83 Subnet::inRange(const isc::asiolink::IOAddress& addr) const {
84     IOAddress first = firstAddrInPrefix(prefix_, prefix_len_);
85     IOAddress last = lastAddrInPrefix(prefix_, prefix_len_);
86 
87     return ((first <= addr) && (addr <= last));
88 }
89 
getLastAllocated(Lease::Type type) const90 isc::asiolink::IOAddress Subnet::getLastAllocated(Lease::Type type) const {
91     if (MultiThreadingMgr::instance().getMode()) {
92         std::lock_guard<std::mutex> lock(*mutex_);
93         return (getLastAllocatedInternal(type));
94     } else {
95         return (getLastAllocatedInternal(type));
96     }
97 }
98 
getLastAllocatedInternal(Lease::Type type) const99 isc::asiolink::IOAddress Subnet::getLastAllocatedInternal(Lease::Type type) const {
100     // check if the type is valid (and throw if it isn't)
101     checkType(type);
102 
103     switch (type) {
104     case Lease::TYPE_V4:
105     case Lease::TYPE_NA:
106         return last_allocated_ia_;
107     case Lease::TYPE_TA:
108         return last_allocated_ta_;
109     case Lease::TYPE_PD:
110         return last_allocated_pd_;
111     default:
112         isc_throw(BadValue, "Pool type " << type << " not supported");
113     }
114 }
115 
116 boost::posix_time::ptime
getLastAllocatedTime(const Lease::Type & lease_type) const117 Subnet::getLastAllocatedTime(const Lease::Type& lease_type) const {
118     if (MultiThreadingMgr::instance().getMode()) {
119         std::lock_guard<std::mutex> lock(*mutex_);
120         return (getLastAllocatedTimeInternal(lease_type));
121     } else {
122         return (getLastAllocatedTimeInternal(lease_type));
123     }
124 }
125 
126 boost::posix_time::ptime
getLastAllocatedTimeInternal(const Lease::Type & lease_type) const127 Subnet::getLastAllocatedTimeInternal(const Lease::Type& lease_type) const {
128     auto t = last_allocated_time_.find(lease_type);
129     if (t != last_allocated_time_.end()) {
130         return (t->second);
131     }
132 
133     // This shouldn't happen, because we have initialized the structure
134     // for all lease types.
135     return (boost::posix_time::neg_infin);
136 }
137 
setLastAllocated(Lease::Type type,const isc::asiolink::IOAddress & addr)138 void Subnet::setLastAllocated(Lease::Type type,
139                               const isc::asiolink::IOAddress& addr) {
140     if (MultiThreadingMgr::instance().getMode()) {
141         std::lock_guard<std::mutex> lock(*mutex_);
142         setLastAllocatedInternal(type, addr);
143     } else {
144         setLastAllocatedInternal(type, addr);
145     }
146 }
147 
setLastAllocatedInternal(Lease::Type type,const isc::asiolink::IOAddress & addr)148 void Subnet::setLastAllocatedInternal(Lease::Type type,
149                                       const isc::asiolink::IOAddress& addr) {
150 
151     // check if the type is valid (and throw if it isn't)
152     checkType(type);
153 
154     switch (type) {
155     case Lease::TYPE_V4:
156     case Lease::TYPE_NA:
157         last_allocated_ia_ = addr;
158         break;
159     case Lease::TYPE_TA:
160         last_allocated_ta_ = addr;
161         break;
162     case Lease::TYPE_PD:
163         last_allocated_pd_ = addr;
164         break;
165     default:
166         isc_throw(BadValue, "Pool type " << type << " not supported");
167     }
168 
169     // Update the timestamp of last allocation.
170     last_allocated_time_[type] = boost::posix_time::microsec_clock::universal_time();
171 }
172 
173 std::string
toText() const174 Subnet::toText() const {
175     std::stringstream tmp;
176     tmp << prefix_ << "/" << static_cast<unsigned int>(prefix_len_);
177     return (tmp.str());
178 }
179 
180 uint64_t
getPoolCapacity(Lease::Type type) const181 Subnet::getPoolCapacity(Lease::Type type) const {
182     switch (type) {
183     case Lease::TYPE_V4:
184     case Lease::TYPE_NA:
185         return sumPoolCapacity(pools_);
186     case Lease::TYPE_TA:
187         return sumPoolCapacity(pools_ta_);
188     case Lease::TYPE_PD:
189         return sumPoolCapacity(pools_pd_);
190     default:
191         isc_throw(BadValue, "Unsupported pool type: "
192                   << static_cast<int>(type));
193     }
194 }
195 
196 uint64_t
getPoolCapacity(Lease::Type type,const ClientClasses & client_classes) const197 Subnet::getPoolCapacity(Lease::Type type,
198                         const ClientClasses& client_classes) const {
199     switch (type) {
200     case Lease::TYPE_V4:
201     case Lease::TYPE_NA:
202         return sumPoolCapacity(pools_, client_classes);
203     case Lease::TYPE_TA:
204         return sumPoolCapacity(pools_ta_, client_classes);
205     case Lease::TYPE_PD:
206         return sumPoolCapacity(pools_pd_, client_classes);
207     default:
208         isc_throw(BadValue, "Unsupported pool type: "
209                   << static_cast<int>(type));
210     }
211 }
212 
213 uint64_t
sumPoolCapacity(const PoolCollection & pools) const214 Subnet::sumPoolCapacity(const PoolCollection& pools) const {
215     uint64_t sum = 0;
216     for (PoolCollection::const_iterator p = pools.begin(); p != pools.end(); ++p) {
217         uint64_t x = (*p)->getCapacity();
218 
219         // Check if we can add it. If sum + x > uint64::max, then we would have
220         // overflown if we tried to add it.
221         if (x > std::numeric_limits<uint64_t>::max() - sum) {
222             return (std::numeric_limits<uint64_t>::max());
223         }
224 
225         sum += x;
226     }
227 
228     return (sum);
229 }
230 
231 uint64_t
sumPoolCapacity(const PoolCollection & pools,const ClientClasses & client_classes) const232 Subnet::sumPoolCapacity(const PoolCollection& pools,
233                         const ClientClasses& client_classes) const {
234     uint64_t sum = 0;
235     for (PoolCollection::const_iterator p = pools.begin(); p != pools.end(); ++p) {
236         if (!(*p)->clientSupported(client_classes)) {
237             continue;
238         }
239         uint64_t x = (*p)->getCapacity();
240 
241         // Check if we can add it. If sum + x > uint64::max, then we would have
242         // overflown if we tried to add it.
243         if (x > std::numeric_limits<uint64_t>::max() - sum) {
244             return (std::numeric_limits<uint64_t>::max());
245         }
246 
247         sum += x;
248     }
249 
250     return (sum);
251 }
252 
253 std::pair<IOAddress, uint8_t>
parsePrefixCommon(const std::string & prefix)254 Subnet::parsePrefixCommon(const std::string& prefix) {
255     auto pos = prefix.find('/');
256     if ((pos == std::string::npos) ||
257         (pos == prefix.size() - 1) ||
258         (pos == 0)) {
259         isc_throw(BadValue, "unable to parse invalid prefix " << prefix);
260     }
261 
262     try {
263         IOAddress address(prefix.substr(0, pos));
264         int length = boost::lexical_cast<int>(prefix.substr(pos + 1));
265         return (std::make_pair(address, static_cast<int>(length)));
266 
267     } catch (...) {
268         isc_throw(BadValue, "unable to parse invalid prefix " << prefix);
269     }
270 }
271 
272 
checkType(Lease::Type type) const273 void Subnet4::checkType(Lease::Type type) const {
274     if (type != Lease::TYPE_V4) {
275         isc_throw(BadValue, "Only TYPE_V4 is allowed for Subnet4");
276     }
277 }
278 
Subnet4(const IOAddress & prefix,uint8_t length,const Triplet<uint32_t> & t1,const Triplet<uint32_t> & t2,const Triplet<uint32_t> & valid_lifetime,const SubnetID id)279 Subnet4::Subnet4(const IOAddress& prefix, uint8_t length,
280                  const Triplet<uint32_t>& t1,
281                  const Triplet<uint32_t>& t2,
282                  const Triplet<uint32_t>& valid_lifetime,
283                  const SubnetID id)
284     : Subnet(prefix, length, id), Network4() {
285     if (!prefix.isV4()) {
286         isc_throw(BadValue, "Non IPv4 prefix " << prefix.toText()
287                   << " specified in subnet4");
288     }
289 
290     // Timers.
291     setT1(t1);
292     setT2(t2);
293     setValid(valid_lifetime);
294 }
295 
296 Subnet4Ptr
create(const IOAddress & prefix,uint8_t length,const Triplet<uint32_t> & t1,const Triplet<uint32_t> & t2,const Triplet<uint32_t> & valid_lifetime,const SubnetID id)297 Subnet4::create(const IOAddress& prefix, uint8_t length,
298                 const Triplet<uint32_t>& t1,
299                 const Triplet<uint32_t>& t2,
300                 const Triplet<uint32_t>& valid_lifetime,
301                 const SubnetID id) {
302     Subnet4Ptr subnet = boost::make_shared<Subnet4>
303         (prefix, length, t1, t2, valid_lifetime, id);
304     return (subnet);
305 }
306 
307 Subnet4Ptr
getNextSubnet(const Subnet4Ptr & first_subnet) const308 Subnet4::getNextSubnet(const Subnet4Ptr& first_subnet) const {
309     SharedNetwork4Ptr network;
310     getSharedNetwork(network);
311     if (network) {
312         return (network->getNextSubnet(first_subnet, getID()));
313     }
314 
315     return (Subnet4Ptr());
316 }
317 
318 Subnet4Ptr
getNextSubnet(const Subnet4Ptr & first_subnet,const ClientClasses & client_classes) const319 Subnet4::getNextSubnet(const Subnet4Ptr& first_subnet,
320                        const ClientClasses& client_classes) const {
321     SharedNetwork4Ptr network;
322     getSharedNetwork(network);
323     // We can only get next subnet if shared network has been defined for
324     // the current subnet.
325     if (network) {
326         Subnet4Ptr subnet;
327         do {
328             // Use subnet identifier of this subnet if this is the first
329             // time we're calling getNextSubnet. Otherwise, use the
330             // subnet id of the previously returned subnet.
331             SubnetID subnet_id = subnet ? subnet->getID() : getID();
332             subnet = network->getNextSubnet(first_subnet, subnet_id);
333             // If client classes match the subnet, return it. Otherwise,
334             // try another subnet.
335             if (subnet && subnet->clientSupported(client_classes)) {
336                 return (subnet);
337             }
338         } while (subnet);
339     }
340 
341     // No subnet found.
342     return (Subnet4Ptr());
343 }
344 
345 
346 bool
clientSupported(const isc::dhcp::ClientClasses & client_classes) const347 Subnet4::clientSupported(const isc::dhcp::ClientClasses& client_classes) const {
348     NetworkPtr network;
349     getSharedNetwork(network);
350     if (network && !network->clientSupported(client_classes)) {
351         return (false);
352     }
353 
354     return (Network4::clientSupported(client_classes));
355 }
356 
getPools(Lease::Type type) const357 const PoolCollection& Subnet::getPools(Lease::Type type) const {
358     // check if the type is valid (and throw if it isn't)
359     checkType(type);
360 
361     switch (type) {
362     case Lease::TYPE_V4:
363     case Lease::TYPE_NA:
364         return (pools_);
365     case Lease::TYPE_TA:
366         return (pools_ta_);
367     case Lease::TYPE_PD:
368         return (pools_pd_);
369     default:
370         isc_throw(BadValue, "Unsupported pool type: "
371                   << static_cast<int>(type));
372     }
373 }
374 
getPoolsWritable(Lease::Type type)375 PoolCollection& Subnet::getPoolsWritable(Lease::Type type) {
376     // check if the type is valid (and throw if it isn't)
377     checkType(type);
378 
379     switch (type) {
380     case Lease::TYPE_V4:
381     case Lease::TYPE_NA:
382         return (pools_);
383     case Lease::TYPE_TA:
384         return (pools_ta_);
385     case Lease::TYPE_PD:
386         return (pools_pd_);
387     default:
388         isc_throw(BadValue, "Invalid pool type specified: "
389                   << static_cast<int>(type));
390     }
391 }
392 
getPool(Lease::Type type,const isc::asiolink::IOAddress & hint,bool anypool) const393 const PoolPtr Subnet::getPool(Lease::Type type, const isc::asiolink::IOAddress& hint,
394                               bool anypool /* true */) const {
395     // check if the type is valid (and throw if it isn't)
396     checkType(type);
397 
398     const PoolCollection& pools = getPools(type);
399 
400     PoolPtr candidate;
401 
402     if (!pools.empty()) {
403         // Pools are sorted by their first prefixes. For example: 2001::,
404         // 2001::db8::, 3000:: etc. If our hint is 2001:db8:5:: we want to
405         // find the pool with the longest matching prefix, so: 2001:db8::,
406         // rather than 2001::. upper_bound returns the first pool with a prefix
407         // that is greater than 2001:db8:5::, i.e. 3000::. To find the longest
408         // matching prefix we use decrement operator to go back by one item.
409         // If returned iterator points to begin it means that prefixes in all
410         // pools are greater than out prefix, and thus there is no match.
411         PoolCollection::const_iterator ub =
412             std::upper_bound(pools.begin(), pools.end(), hint,
413                              prefixLessThanFirstAddress);
414 
415         if (ub != pools.begin()) {
416             --ub;
417             if ((*ub)->inRange(hint)) {
418                 candidate = *ub;
419             }
420         }
421 
422         // If we don't find anything better, then let's just use the first pool
423         if (!candidate && anypool) {
424             candidate = *pools.begin();
425         }
426     }
427 
428     // Return a pool or NULL if no match found.
429     return (candidate);
430 }
431 
getPool(Lease::Type type,const ClientClasses & client_classes,const isc::asiolink::IOAddress & hint) const432 const PoolPtr Subnet::getPool(Lease::Type type,
433                               const ClientClasses& client_classes,
434                               const isc::asiolink::IOAddress& hint) const {
435     // check if the type is valid (and throw if it isn't)
436     checkType(type);
437 
438     const PoolCollection& pools = getPools(type);
439 
440     PoolPtr candidate;
441 
442     if (!pools.empty()) {
443         PoolCollection::const_iterator ub =
444             std::upper_bound(pools.begin(), pools.end(), hint,
445                              prefixLessThanFirstAddress);
446 
447         if (ub != pools.begin()) {
448             --ub;
449             if ((*ub)->inRange(hint) &&
450                 (*ub)->clientSupported(client_classes)) {
451                 candidate = *ub;
452             }
453         }
454     }
455 
456     // Return a pool or NULL if no match found.
457     return (candidate);
458 }
459 
460 void
addPool(const PoolPtr & pool)461 Subnet::addPool(const PoolPtr& pool) {
462     // check if the type is valid (and throw if it isn't)
463     checkType(pool->getType());
464 
465     // Check that the pool is in range with a subnet only if this is
466     // not a pool of IPv6 prefixes. The IPv6 prefixes delegated for
467     // the particular subnet don't need to match the prefix of the
468     // subnet.
469     if (pool->getType() != Lease::TYPE_PD) {
470         if (!inRange(pool->getFirstAddress()) || !inRange(pool->getLastAddress())) {
471             isc_throw(BadValue, "a pool of type "
472                       << Lease::typeToText(pool->getType())
473                       << ", with the following address range: "
474                       << pool->getFirstAddress() << "-"
475                       << pool->getLastAddress() << " does not match"
476                       << " the prefix of a subnet: "
477                       << prefix_ << "/" << static_cast<int>(prefix_len_)
478                       << " to which it is being added");
479 
480         }
481     }
482 
483     bool overlaps = false;
484     if (pool->getType() == Lease::TYPE_V4) {
485         overlaps = poolOverlaps(Lease::TYPE_V4, pool);
486 
487     } else {
488         overlaps =
489             poolOverlaps(Lease::TYPE_NA, pool) ||
490             poolOverlaps(Lease::TYPE_PD, pool) ||
491             poolOverlaps(Lease::TYPE_TA, pool);
492     }
493 
494     if (overlaps) {
495         isc_throw(BadValue,"a pool of type "
496                   << Lease::typeToText(pool->getType())
497                   << ", with the following address range: "
498                   << pool->getFirstAddress() << "-"
499                   << pool->getLastAddress() << " overlaps with "
500                   "an existing pool in the subnet: "
501                   << prefix_ << "/" << static_cast<int>(prefix_len_)
502                   << " to which it is being added");
503     }
504 
505     PoolCollection& pools_writable = getPoolsWritable(pool->getType());
506 
507     // Add the pool to the appropriate pools collection
508     pools_writable.push_back(pool);
509 
510     // Sort pools by first address.
511     std::sort(pools_writable.begin(), pools_writable.end(),
512               comparePoolFirstAddress);
513 }
514 
515 void
delPools(Lease::Type type)516 Subnet::delPools(Lease::Type type) {
517     getPoolsWritable(type).clear();
518 }
519 
520 bool
inPool(Lease::Type type,const isc::asiolink::IOAddress & addr) const521 Subnet::inPool(Lease::Type type, const isc::asiolink::IOAddress& addr) const {
522 
523     // Let's start with checking if it even belongs to that subnet.
524     if ((type != Lease::TYPE_PD) && !inRange(addr)) {
525         return (false);
526     }
527 
528     const PoolCollection& pools = getPools(type);
529 
530     for (PoolCollection::const_iterator pool = pools.begin();
531          pool != pools.end(); ++pool) {
532         if ((*pool)->inRange(addr)) {
533             return (true);
534         }
535     }
536     // There's no pool that address belongs to
537     return (false);
538 }
539 
540 bool
inPool(Lease::Type type,const isc::asiolink::IOAddress & addr,const ClientClasses & client_classes) const541 Subnet::inPool(Lease::Type type,
542                const isc::asiolink::IOAddress& addr,
543                const ClientClasses& client_classes) const {
544 
545     // Let's start with checking if it even belongs to that subnet.
546     if ((type != Lease::TYPE_PD) && !inRange(addr)) {
547         return (false);
548     }
549 
550     const PoolCollection& pools = getPools(type);
551 
552     for (PoolCollection::const_iterator pool = pools.begin();
553          pool != pools.end(); ++pool) {
554         if (!(*pool)->clientSupported(client_classes)) {
555             continue;
556         }
557         if ((*pool)->inRange(addr)) {
558             return (true);
559         }
560     }
561     // There's no pool that address belongs to
562     return (false);
563 }
564 
565 bool
poolOverlaps(const Lease::Type & pool_type,const PoolPtr & pool) const566 Subnet::poolOverlaps(const Lease::Type& pool_type, const PoolPtr& pool) const {
567     const PoolCollection& pools = getPools(pool_type);
568 
569     // If no pools, we don't overlap. Nothing to do.
570     if (pools.empty()) {
571         return (false);
572     }
573 
574     // We're going to insert a new pool, likely between two existing pools.
575     // So we're going to end up with the following case:
576     // |<---- pool1 ---->|    |<-------- pool2 ------>|  |<-- pool3 -->|
577     // F1               L1    F2                     L2  F3           L3
578     // where pool1 and pool3 are existing pools, pool2 is a pool being
579     // inserted and "F"/"L" mark first and last address in the pools
580     // respectively. So the following conditions must be fulfilled:
581     // F2 > L1 and L2 < F3. Obviously, for any pool: F < L.
582 
583     // Search for pool3. We use F2 and upper_bound to find the F3 (upper_bound
584     // returns first pool in the sorted container which first address is
585     // greater than F2). prefixLessThanPoolAddress with the first argument
586     // set to "true" is the custom comparison function for upper_bound, which
587     // compares F2 with the first addresses of the existing pools.
588     PoolCollection::const_iterator pool3_it =
589         std::upper_bound(pools.begin(), pools.end(), pool->getFirstAddress(),
590                          prefixLessThanFirstAddress);
591 
592     // upper_bound returns a first pool which first address is greater than the
593     // address F2. However, it is also possible that there is a pool which first
594     // address is equal to F2. Such pool is also in conflict with a new pool.
595     // If the returned value is pools.begin() it means that all pools have greater
596     // first address than F2, thus none of the pools can have first address equal
597     // to F2. Otherwise, we'd need to check them for equality.
598     if (pool3_it != pools.begin()) {
599         // Go back one pool and check if addresses are equal.
600         PoolPtr pool3 = *(pool3_it - 1);
601         if (pool3->getFirstAddress() == pool->getFirstAddress()) {
602             return (true);
603         }
604     }
605 
606     // If returned value is unequal pools.end() it means that there is a pool3,
607     // with F3 > F2.
608     if (pool3_it != pools.end()) {
609         // Let's store the pointer to this pool.
610         PoolPtr pool3 = *pool3_it;
611         // F3 must be greater than L2, otherwise pools will overlap.
612         if (pool3->getFirstAddress() <= pool->getLastAddress()) {
613             return (true);
614         }
615     }
616 
617     // If L2 is ok, we now have to find the pool1. This pool should be
618     // right before the pool3 if there is any pool before pool3.
619     if (pool3_it != pools.begin()) {
620         PoolPtr pool1 = *(pool3_it - 1);
621         // F2 must be greater than L1.
622         if (pool->getFirstAddress() <= pool1->getLastAddress()) {
623             return (true);
624         }
625     }
626 
627     return (false);
628 }
629 
630 
Subnet6(const IOAddress & prefix,uint8_t length,const Triplet<uint32_t> & t1,const Triplet<uint32_t> & t2,const Triplet<uint32_t> & preferred_lifetime,const Triplet<uint32_t> & valid_lifetime,const SubnetID id)631 Subnet6::Subnet6(const IOAddress& prefix, uint8_t length,
632                  const Triplet<uint32_t>& t1,
633                  const Triplet<uint32_t>& t2,
634                  const Triplet<uint32_t>& preferred_lifetime,
635                  const Triplet<uint32_t>& valid_lifetime,
636                  const SubnetID id)
637     : Subnet(prefix, length, id), Network6() {
638     if (!prefix.isV6()) {
639         isc_throw(BadValue, "Non IPv6 prefix " << prefix
640                   << " specified in subnet6");
641     }
642 
643     // Timers.
644     setT1(t1);
645     setT2(t2);
646     setPreferred(preferred_lifetime);
647     setValid(valid_lifetime);
648 }
649 
650 Subnet6Ptr
create(const IOAddress & prefix,uint8_t length,const Triplet<uint32_t> & t1,const Triplet<uint32_t> & t2,const Triplet<uint32_t> & preferred_lifetime,const Triplet<uint32_t> & valid_lifetime,const SubnetID id)651 Subnet6::create(const IOAddress& prefix, uint8_t length,
652                 const Triplet<uint32_t>& t1,
653                 const Triplet<uint32_t>& t2,
654                 const Triplet<uint32_t>& preferred_lifetime,
655                 const Triplet<uint32_t>& valid_lifetime,
656                 const SubnetID id) {
657     Subnet6Ptr subnet = boost::make_shared<Subnet6>
658         (prefix, length, t1, t2, preferred_lifetime, valid_lifetime, id);
659     return (subnet);
660 }
661 
checkType(Lease::Type type) const662 void Subnet6::checkType(Lease::Type type) const {
663     if ( (type != Lease::TYPE_NA) && (type != Lease::TYPE_TA) &&
664          (type != Lease::TYPE_PD)) {
665         isc_throw(BadValue, "Invalid Pool type: " << Lease::typeToText(type)
666                   << "(" << static_cast<int>(type)
667                   << "), must be TYPE_NA, TYPE_TA or TYPE_PD for Subnet6");
668     }
669 }
670 
671 Subnet6Ptr
getNextSubnet(const Subnet6Ptr & first_subnet) const672 Subnet6::getNextSubnet(const Subnet6Ptr& first_subnet) const {
673     SharedNetwork6Ptr network;
674     getSharedNetwork(network);
675     if (network) {
676         return (network->getNextSubnet(first_subnet, getID()));
677     }
678 
679     return (Subnet6Ptr());
680 }
681 
682 Subnet6Ptr
getNextSubnet(const Subnet6Ptr & first_subnet,const ClientClasses & client_classes) const683 Subnet6::getNextSubnet(const Subnet6Ptr& first_subnet,
684                        const ClientClasses& client_classes) const {
685     SharedNetwork6Ptr network;
686     getSharedNetwork(network);
687     // We can only get next subnet if shared network has been defined for
688     // the current subnet.
689     if (network) {
690         Subnet6Ptr subnet;
691         do {
692             // Use subnet identifier of this subnet if this is the first
693             // time we're calling getNextSubnet. Otherwise, use the
694             // subnet id of the previously returned subnet.
695             SubnetID subnet_id = subnet ? subnet->getID() : getID();
696             subnet = network->getNextSubnet(first_subnet, subnet_id);
697             // If client classes match the subnet, return it. Otherwise,
698             // try another subnet.
699             if (subnet && subnet->clientSupported(client_classes)) {
700                 return (subnet);
701             }
702         } while (subnet);
703     }
704 
705     // No subnet found.
706     return (Subnet6Ptr());
707 }
708 
709 bool
clientSupported(const isc::dhcp::ClientClasses & client_classes) const710 Subnet6::clientSupported(const isc::dhcp::ClientClasses& client_classes) const {
711     NetworkPtr network;
712     getSharedNetwork(network);
713     if (network && !network->clientSupported(client_classes)) {
714         return (false);
715     }
716 
717     return (Network6::clientSupported(client_classes));
718 }
719 
720 data::ElementPtr
toElement() const721 Subnet::toElement() const {
722     ElementPtr map = Element::createMap();
723 
724     // Add user-context
725     contextToElement(map);
726 
727     // Set subnet id
728     SubnetID id = getID();
729     map->set("id", Element::create(static_cast<long long>(id)));
730 
731     // Set subnet
732     map->set("subnet", Element::create(toText()));
733 
734     return (map);
735 }
736 
737 data::ElementPtr
toElement() const738 Subnet4::toElement() const {
739     // Prepare the map
740     ElementPtr map = Subnet::toElement();
741     ElementPtr network_map = Network4::toElement();
742 
743     merge(map, network_map);
744 
745     // Set DHCP4o6
746     const Cfg4o6& d4o6 = get4o6();
747     isc::data::merge(map, d4o6.toElement());
748 
749     // Set pools
750     const PoolCollection& pools = getPools(Lease::TYPE_V4);
751     ElementPtr pool_list = Element::createList();
752     for (PoolCollection::const_iterator pool = pools.cbegin();
753          pool != pools.cend(); ++pool) {
754         // Add the elementized pool to the list
755         pool_list->add((*pool)->toElement());
756     }
757     map->set("pools", pool_list);
758 
759     return (map);
760 }
761 
762 std::pair<IOAddress, uint8_t>
parsePrefix(const std::string & prefix)763 Subnet4::parsePrefix(const std::string& prefix) {
764     std::pair<IOAddress, uint8_t> parsed = Subnet::parsePrefixCommon(prefix);
765     if (!parsed.first.isV4() || parsed.first.isV4Zero() ||
766         (parsed.second > 32) || (parsed.second == 0)) {
767         isc_throw(BadValue, "unable to parse invalid IPv4 prefix " << prefix);
768     }
769     return (parsed);
770 }
771 
772 data::ElementPtr
toElement() const773 Subnet6::toElement() const {
774     // Prepare the map
775     ElementPtr map = Subnet::toElement();
776     ElementPtr network_map = Network6::toElement();
777 
778     merge(map, network_map);
779 
780     // Set pools
781     const PoolCollection& pools = getPools(Lease::TYPE_NA);
782     ElementPtr pool_list = Element::createList();
783     for (PoolCollection::const_iterator pool = pools.cbegin();
784          pool != pools.cend(); ++pool) {
785         // Add the elementized pool to the list
786         pool_list->add((*pool)->toElement());
787     }
788     map->set("pools", pool_list);
789 
790     // Set pd-pools
791     const PoolCollection& pdpools = getPools(Lease::TYPE_PD);
792     ElementPtr pdpool_list = Element::createList();
793     for (PoolCollection::const_iterator pool = pdpools.cbegin();
794          pool != pdpools.cend(); ++pool) {
795         // Add the elementized pool to the list
796         pdpool_list->add((*pool)->toElement());
797     }
798     map->set("pd-pools", pdpool_list);
799 
800     return (map);
801 }
802 
803 std::pair<IOAddress, uint8_t>
parsePrefix(const std::string & prefix)804 Subnet6::parsePrefix(const std::string& prefix) {
805     std::pair<IOAddress, uint8_t> parsed = Subnet::parsePrefixCommon(prefix);
806     if (!parsed.first.isV6() || parsed.first.isV6Zero() ||
807         (parsed.second > 128) || (parsed.second == 0)) {
808         isc_throw(BadValue, "unable to parse invalid IPv6 prefix " << prefix);
809     }
810     return (parsed);
811 }
812 
813 } // end of isc::dhcp namespace
814 } // end of isc namespace
815