subnet.cc 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611
  1. // Copyright (C) 2012-2017 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. #include <config.h>
  7. #include <asiolink/io_address.h>
  8. #include <dhcp/option_space.h>
  9. #include <dhcpsrv/addr_utilities.h>
  10. #include <dhcpsrv/subnet.h>
  11. #include <algorithm>
  12. #include <sstream>
  13. using namespace isc::asiolink;
  14. using namespace isc::data;
  15. using namespace isc::dhcp;
  16. namespace {
  17. /// @brief Function used in calls to std::upper_bound to check
  18. /// if the specified prefix is lower than the first address a pool.
  19. ///
  20. /// @return true if prefix is lower than the first address in the pool.
  21. bool
  22. prefixLessThanFirstAddress(const IOAddress& prefix, const PoolPtr& pool) {
  23. return (prefix < pool->getFirstAddress());
  24. }
  25. /// @brief Function used in calls to std::sort to compare first
  26. /// prefixes of the two pools.
  27. ///
  28. /// @param pool1 First pool.
  29. /// @param pool2 Second pool.
  30. ///
  31. /// @return true if first prefix of the first pool is smaller than
  32. /// the first address of the second pool.
  33. bool
  34. comparePoolFirstAddress(const PoolPtr& pool1, const PoolPtr& pool2) {
  35. return (pool1->getFirstAddress() < pool2->getFirstAddress());
  36. };
  37. }
  38. namespace isc {
  39. namespace dhcp {
  40. // This is an initial value of subnet-id. See comments in subnet.h for details.
  41. SubnetID Subnet::static_id_ = 1;
  42. Subnet::Subnet(const isc::asiolink::IOAddress& prefix, uint8_t len,
  43. const SubnetID id)
  44. : id_(id == 0 ? generateNextID() : id), prefix_(prefix),
  45. prefix_len_(len),
  46. last_allocated_ia_(lastAddrInPrefix(prefix, len)),
  47. last_allocated_ta_(lastAddrInPrefix(prefix, len)),
  48. last_allocated_pd_(lastAddrInPrefix(prefix, len)) {
  49. if ((prefix.isV6() && len > 128) ||
  50. (prefix.isV4() && len > 32)) {
  51. isc_throw(BadValue,
  52. "Invalid prefix length specified for subnet: " << len);
  53. }
  54. }
  55. bool
  56. Subnet::inRange(const isc::asiolink::IOAddress& addr) const {
  57. IOAddress first = firstAddrInPrefix(prefix_, prefix_len_);
  58. IOAddress last = lastAddrInPrefix(prefix_, prefix_len_);
  59. return ((first <= addr) && (addr <= last));
  60. }
  61. isc::asiolink::IOAddress Subnet::getLastAllocated(Lease::Type type) const {
  62. // check if the type is valid (and throw if it isn't)
  63. checkType(type);
  64. switch (type) {
  65. case Lease::TYPE_V4:
  66. case Lease::TYPE_NA:
  67. return last_allocated_ia_;
  68. case Lease::TYPE_TA:
  69. return last_allocated_ta_;
  70. case Lease::TYPE_PD:
  71. return last_allocated_pd_;
  72. default:
  73. isc_throw(BadValue, "Pool type " << type << " not supported");
  74. }
  75. }
  76. void Subnet::setLastAllocated(Lease::Type type,
  77. const isc::asiolink::IOAddress& addr) {
  78. // check if the type is valid (and throw if it isn't)
  79. checkType(type);
  80. switch (type) {
  81. case Lease::TYPE_V4:
  82. case Lease::TYPE_NA:
  83. last_allocated_ia_ = addr;
  84. return;
  85. case Lease::TYPE_TA:
  86. last_allocated_ta_ = addr;
  87. return;
  88. case Lease::TYPE_PD:
  89. last_allocated_pd_ = addr;
  90. return;
  91. default:
  92. isc_throw(BadValue, "Pool type " << type << " not supported");
  93. }
  94. }
  95. std::string
  96. Subnet::toText() const {
  97. std::stringstream tmp;
  98. tmp << prefix_ << "/" << static_cast<unsigned int>(prefix_len_);
  99. return (tmp.str());
  100. }
  101. uint64_t
  102. Subnet::getPoolCapacity(Lease::Type type) const {
  103. switch (type) {
  104. case Lease::TYPE_V4:
  105. case Lease::TYPE_NA:
  106. return sumPoolCapacity(pools_);
  107. case Lease::TYPE_TA:
  108. return sumPoolCapacity(pools_ta_);
  109. case Lease::TYPE_PD:
  110. return sumPoolCapacity(pools_pd_);
  111. default:
  112. isc_throw(BadValue, "Unsupported pool type: "
  113. << static_cast<int>(type));
  114. }
  115. }
  116. uint64_t
  117. Subnet::sumPoolCapacity(const PoolCollection& pools) const {
  118. uint64_t sum = 0;
  119. for (PoolCollection::const_iterator p = pools.begin(); p != pools.end(); ++p) {
  120. uint64_t x = (*p)->getCapacity();
  121. // Check if we can add it. If sum + x > uint64::max, then we would have
  122. // overflown if we tried to add it.
  123. if (x > std::numeric_limits<uint64_t>::max() - sum) {
  124. return (std::numeric_limits<uint64_t>::max());
  125. }
  126. sum += x;
  127. }
  128. return (sum);
  129. }
  130. void Subnet4::checkType(Lease::Type type) const {
  131. if (type != Lease::TYPE_V4) {
  132. isc_throw(BadValue, "Only TYPE_V4 is allowed for Subnet4");
  133. }
  134. }
  135. Subnet4::Subnet4(const isc::asiolink::IOAddress& prefix, uint8_t length,
  136. const Triplet<uint32_t>& t1,
  137. const Triplet<uint32_t>& t2,
  138. const Triplet<uint32_t>& valid_lifetime,
  139. const SubnetID id)
  140. : Subnet(prefix, length, id), Network4(),
  141. siaddr_(IOAddress("0.0.0.0")) {
  142. if (!prefix.isV4()) {
  143. isc_throw(BadValue, "Non IPv4 prefix " << prefix.toText()
  144. << " specified in subnet4");
  145. }
  146. // Relay info.
  147. setRelayInfo(IOAddress::IPV4_ZERO_ADDRESS());
  148. // Timers.
  149. setT1(t1);
  150. setT2(t2);
  151. setValid(valid_lifetime);
  152. }
  153. bool
  154. Subnet4::clientSupported(const isc::dhcp::ClientClasses& client_classes) const {
  155. NetworkPtr network;
  156. getSharedNetwork(network);
  157. if (network && !network->clientSupported(client_classes)) {
  158. return (false);
  159. }
  160. return (Network4::clientSupported(client_classes));
  161. }
  162. void Subnet4::setSiaddr(const isc::asiolink::IOAddress& siaddr) {
  163. if (!siaddr.isV4()) {
  164. isc_throw(BadValue, "Can't set siaddr to non-IPv4 address "
  165. << siaddr);
  166. }
  167. siaddr_ = siaddr;
  168. }
  169. isc::asiolink::IOAddress Subnet4::getSiaddr() const {
  170. return (siaddr_);
  171. }
  172. const PoolCollection& Subnet::getPools(Lease::Type type) const {
  173. // check if the type is valid (and throw if it isn't)
  174. checkType(type);
  175. switch (type) {
  176. case Lease::TYPE_V4:
  177. case Lease::TYPE_NA:
  178. return (pools_);
  179. case Lease::TYPE_TA:
  180. return (pools_ta_);
  181. case Lease::TYPE_PD:
  182. return (pools_pd_);
  183. default:
  184. isc_throw(BadValue, "Unsupported pool type: "
  185. << static_cast<int>(type));
  186. }
  187. }
  188. PoolCollection& Subnet::getPoolsWritable(Lease::Type type) {
  189. // check if the type is valid (and throw if it isn't)
  190. checkType(type);
  191. switch (type) {
  192. case Lease::TYPE_V4:
  193. case Lease::TYPE_NA:
  194. return (pools_);
  195. case Lease::TYPE_TA:
  196. return (pools_ta_);
  197. case Lease::TYPE_PD:
  198. return (pools_pd_);
  199. default:
  200. isc_throw(BadValue, "Invalid pool type specified: "
  201. << static_cast<int>(type));
  202. }
  203. }
  204. const PoolPtr Subnet::getPool(Lease::Type type, const isc::asiolink::IOAddress& hint,
  205. bool anypool /* true */) const {
  206. // check if the type is valid (and throw if it isn't)
  207. checkType(type);
  208. const PoolCollection& pools = getPools(type);
  209. PoolPtr candidate;
  210. if (!pools.empty()) {
  211. // Pools are sorted by their first prefixes. For example: 2001::,
  212. // 2001::db8::, 3000:: etc. If our hint is 2001:db8:5:: we want to
  213. // find the pool with the longest matching prefix, so: 2001:db8::,
  214. // rather than 2001::. upper_bound returns the first pool with a prefix
  215. // that is greater than 2001:db8:5::, i.e. 3000::. To find the longest
  216. // matching prefix we use decrement operator to go back by one item.
  217. // If returned iterator points to begin it means that prefixes in all
  218. // pools are greater than out prefix, and thus there is no match.
  219. PoolCollection::const_iterator ub =
  220. std::upper_bound(pools.begin(), pools.end(), hint,
  221. prefixLessThanFirstAddress);
  222. if (ub != pools.begin()) {
  223. --ub;
  224. if ((*ub)->inRange(hint)) {
  225. candidate = *ub;
  226. }
  227. }
  228. // If we don't find anything better, then let's just use the first pool
  229. if (!candidate && anypool) {
  230. candidate = *pools.begin();
  231. }
  232. }
  233. // Return a pool or NULL if no match found.
  234. return (candidate);
  235. }
  236. void
  237. Subnet::addPool(const PoolPtr& pool) {
  238. // check if the type is valid (and throw if it isn't)
  239. checkType(pool->getType());
  240. // Check that the pool is in range with a subnet only if this is
  241. // not a pool of IPv6 prefixes. The IPv6 prefixes delegated for
  242. // the particular subnet don't need to match the prefix of the
  243. // subnet.
  244. if (pool->getType() != Lease::TYPE_PD) {
  245. if (!inRange(pool->getFirstAddress()) || !inRange(pool->getLastAddress())) {
  246. isc_throw(BadValue, "a pool of type "
  247. << Lease::typeToText(pool->getType())
  248. << ", with the following address range: "
  249. << pool->getFirstAddress() << "-"
  250. << pool->getLastAddress() << " does not match"
  251. << " the prefix of a subnet: "
  252. << prefix_ << "/" << static_cast<int>(prefix_len_)
  253. << " to which it is being added");
  254. }
  255. }
  256. bool overlaps = false;
  257. if (pool->getType() == Lease::TYPE_V4) {
  258. overlaps = poolOverlaps(Lease::TYPE_V4, pool);
  259. } else {
  260. overlaps =
  261. poolOverlaps(Lease::TYPE_NA, pool) ||
  262. poolOverlaps(Lease::TYPE_PD, pool) ||
  263. poolOverlaps(Lease::TYPE_TA, pool);
  264. }
  265. if (overlaps) {
  266. isc_throw(BadValue,"a pool of type "
  267. << Lease::typeToText(pool->getType())
  268. << ", with the following address range: "
  269. << pool->getFirstAddress() << "-"
  270. << pool->getLastAddress() << " overlaps with "
  271. "an existing pool in the subnet: "
  272. << prefix_ << "/" << static_cast<int>(prefix_len_)
  273. << " to which it is being added");
  274. }
  275. PoolCollection& pools_writable = getPoolsWritable(pool->getType());
  276. // Add the pool to the appropriate pools collection
  277. pools_writable.push_back(pool);
  278. // Sort pools by first address.
  279. std::sort(pools_writable.begin(), pools_writable.end(),
  280. comparePoolFirstAddress);
  281. }
  282. void
  283. Subnet::delPools(Lease::Type type) {
  284. getPoolsWritable(type).clear();
  285. }
  286. bool
  287. Subnet::inPool(Lease::Type type, const isc::asiolink::IOAddress& addr) const {
  288. // Let's start with checking if it even belongs to that subnet.
  289. if ((type != Lease::TYPE_PD) && !inRange(addr)) {
  290. return (false);
  291. }
  292. const PoolCollection& pools = getPools(type);
  293. for (PoolCollection::const_iterator pool = pools.begin();
  294. pool != pools.end(); ++pool) {
  295. if ((*pool)->inRange(addr)) {
  296. return (true);
  297. }
  298. }
  299. // There's no pool that address belongs to
  300. return (false);
  301. }
  302. bool
  303. Subnet::poolOverlaps(const Lease::Type& pool_type, const PoolPtr& pool) const {
  304. const PoolCollection& pools = getPools(pool_type);
  305. // If no pools, we don't overlap. Nothing to do.
  306. if (pools.empty()) {
  307. return (false);
  308. }
  309. // We're going to insert a new pool, likely between two existing pools.
  310. // So we're going to end up with the following case:
  311. // |<---- pool1 ---->| |<-------- pool2 ------>| |<-- pool3 -->|
  312. // F1 L1 F2 L2 F3 L3
  313. // where pool1 and pool3 are existing pools, pool2 is a pool being
  314. // inserted and "F"/"L" mark first and last address in the pools
  315. // respectively. So the following conditions must be fulfilled:
  316. // F2 > L1 and L2 < F3. Obviously, for any pool: F < L.
  317. // Search for pool3. We use F2 and upper_bound to find the F3 (upper_bound
  318. // returns first pool in the sorted container which first address is
  319. // greater than F2). prefixLessThanPoolAddress with the first argument
  320. // set to "true" is the custom comparison function for upper_bound, which
  321. // compares F2 with the first addresses of the existing pools.
  322. PoolCollection::const_iterator pool3_it =
  323. std::upper_bound(pools.begin(), pools.end(), pool->getFirstAddress(),
  324. prefixLessThanFirstAddress);
  325. // upper_bound returns a first pool which first address is greater than the
  326. // address F2. However, it is also possible that there is a pool which first
  327. // address is equal to F2. Such pool is also in conflict with a new pool.
  328. // If the returned value is pools.begin() it means that all pools have greater
  329. // first address than F2, thus none of the pools can have first address equal
  330. // to F2. Otherwise, we'd need to check them for equality.
  331. if (pool3_it != pools.begin()) {
  332. // Go back one pool and check if addresses are equal.
  333. PoolPtr pool3 = *(pool3_it - 1);
  334. if (pool3->getFirstAddress() == pool->getFirstAddress()) {
  335. return (true);
  336. }
  337. }
  338. // If returned value is unequal pools.end() it means that there is a pool3,
  339. // with F3 > F2.
  340. if (pool3_it != pools.end()) {
  341. // Let's store the pointer to this pool.
  342. PoolPtr pool3 = *pool3_it;
  343. // F3 must be greater than L2, otherwise pools will overlap.
  344. if (pool3->getFirstAddress() <= pool->getLastAddress()) {
  345. return (true);
  346. }
  347. }
  348. // If L2 is ok, we now have to find the pool1. This pool should be
  349. // right before the pool3 if there is any pool before pool3.
  350. if (pool3_it != pools.begin()) {
  351. PoolPtr pool1 = *(pool3_it - 1);
  352. // F2 must be greater than L1.
  353. if (pool->getFirstAddress() <= pool1->getLastAddress()) {
  354. return (true);
  355. }
  356. }
  357. return (false);
  358. }
  359. Subnet6::Subnet6(const isc::asiolink::IOAddress& prefix, uint8_t length,
  360. const Triplet<uint32_t>& t1,
  361. const Triplet<uint32_t>& t2,
  362. const Triplet<uint32_t>& preferred_lifetime,
  363. const Triplet<uint32_t>& valid_lifetime,
  364. const SubnetID id)
  365. : Subnet(prefix, length, id), Network6() {
  366. if (!prefix.isV6()) {
  367. isc_throw(BadValue, "Non IPv6 prefix " << prefix
  368. << " specified in subnet6");
  369. }
  370. // Relay info.
  371. setRelayInfo(RelayInfo(IOAddress::IPV6_ZERO_ADDRESS()));
  372. // Timers.
  373. setT1(t1);
  374. setT2(t2);
  375. setPreferred(preferred_lifetime);
  376. setValid(valid_lifetime);
  377. }
  378. void Subnet6::checkType(Lease::Type type) const {
  379. if ( (type != Lease::TYPE_NA) && (type != Lease::TYPE_TA) &&
  380. (type != Lease::TYPE_PD)) {
  381. isc_throw(BadValue, "Invalid Pool type: " << Lease::typeToText(type)
  382. << "(" << static_cast<int>(type)
  383. << "), must be TYPE_NA, TYPE_TA or TYPE_PD for Subnet6");
  384. }
  385. }
  386. bool
  387. Subnet6::clientSupported(const isc::dhcp::ClientClasses& client_classes) const {
  388. NetworkPtr network;
  389. getSharedNetwork(network);
  390. if (network && !network->clientSupported(client_classes)) {
  391. return (false);
  392. }
  393. return (Network6::clientSupported(client_classes));
  394. }
  395. data::ElementPtr
  396. Subnet::toElement() const {
  397. ElementPtr map = Element::createMap();
  398. // Set subnet id
  399. SubnetID id = getID();
  400. map->set("id", Element::create(static_cast<long long>(id)));
  401. // Set subnet
  402. map->set("subnet", Element::create(toText()));
  403. // Add user-context, but only if defined. Omit if it was not.
  404. ConstElementPtr ctx = getContext();
  405. if (ctx) {
  406. map->set("user-context", ctx);
  407. }
  408. return (map);
  409. }
  410. data::ElementPtr
  411. Subnet4::toElement() const {
  412. // Prepare the map
  413. ElementPtr map = Subnet::toElement();
  414. ElementPtr network_map = Network4::toElement();
  415. merge(map, network_map);
  416. // Set DHCP4o6
  417. const Cfg4o6& d4o6 = get4o6();
  418. isc::data::merge(map, d4o6.toElement());
  419. // Set next-server
  420. map->set("next-server", Element::create(getSiaddr().toText()));
  421. // Set pools
  422. const PoolCollection& pools = getPools(Lease::TYPE_V4);
  423. ElementPtr pool_list = Element::createList();
  424. for (PoolCollection::const_iterator pool = pools.cbegin();
  425. pool != pools.cend(); ++pool) {
  426. // Add the elementized pool to the list
  427. pool_list->add((*pool)->toElement());
  428. }
  429. map->set("pools", pool_list);
  430. return (map);
  431. }
  432. data::ElementPtr
  433. Subnet6::toElement() const {
  434. // Prepare the map
  435. ElementPtr map = Subnet::toElement();
  436. ElementPtr network_map = Network6::toElement();
  437. merge(map, network_map);
  438. // Set pools
  439. const PoolCollection& pools = getPools(Lease::TYPE_NA);
  440. ElementPtr pool_list = Element::createList();
  441. for (PoolCollection::const_iterator pool = pools.cbegin();
  442. pool != pools.cend(); ++pool) {
  443. // Prepare the map for a pool (@todo move this code to pool.cc)
  444. ElementPtr pool_map = Element::createMap();
  445. // Set pool
  446. const IOAddress& first = (*pool)->getFirstAddress();
  447. const IOAddress& last = (*pool)->getLastAddress();
  448. std::string range = first.toText() + "-" + last.toText();
  449. // Try to output a prefix (vs a range)
  450. int prefix_len = prefixLengthFromRange(first, last);
  451. if (prefix_len >= 0) {
  452. std::ostringstream oss;
  453. oss << first.toText() << "/" << prefix_len;
  454. range = oss.str();
  455. }
  456. pool_map->set("pool", Element::create(range));
  457. // Set user-context
  458. ConstElementPtr context = (*pool)->getContext();
  459. if (!isNull(context)) {
  460. pool_map->set("user-context", context);
  461. }
  462. // Set pool options
  463. ConstCfgOptionPtr opts = (*pool)->getCfgOption();
  464. pool_map->set("option-data", opts->toElement());
  465. // Push on the pool list
  466. pool_list->add(pool_map);
  467. }
  468. map->set("pools", pool_list);
  469. // Set pd-pools
  470. const PoolCollection& pdpools = getPools(Lease::TYPE_PD);
  471. ElementPtr pdpool_list = Element::createList();
  472. for (PoolCollection::const_iterator pool = pdpools.cbegin();
  473. pool != pdpools.cend(); ++pool) {
  474. // Get it as a Pool6 (@todo move this code to pool.cc)
  475. const Pool6* pdpool = dynamic_cast<Pool6*>(pool->get());
  476. if (!pdpool) {
  477. isc_throw(ToElementError, "invalid pd-pool pointer");
  478. }
  479. // Prepare the map for a pd-pool
  480. ElementPtr pool_map = Element::createMap();
  481. // Set prefix
  482. const IOAddress& prefix = pdpool->getFirstAddress();
  483. pool_map->set("prefix", Element::create(prefix.toText()));
  484. // Set prefix-len (get it from min - max)
  485. const IOAddress& last = pdpool->getLastAddress();
  486. int prefix_len = prefixLengthFromRange(prefix, last);
  487. if (prefix_len < 0) {
  488. // The pool is bad: give up
  489. isc_throw(ToElementError, "invalid prefix range "
  490. << prefix.toText() << "-" << last.toText());
  491. }
  492. pool_map->set("prefix-len", Element::create(prefix_len));
  493. // Set delegated-len
  494. uint8_t len = pdpool->getLength();
  495. pool_map->set("delegated-len",
  496. Element::create(static_cast<int>(len)));
  497. // Set excluded prefix
  498. const Option6PDExcludePtr& xopt =
  499. pdpool->getPrefixExcludeOption();
  500. if (xopt) {
  501. const IOAddress& xprefix =
  502. xopt->getExcludedPrefix(prefix, len);
  503. pool_map->set("excluded-prefix",
  504. Element::create(xprefix.toText()));
  505. uint8_t xlen = xopt->getExcludedPrefixLength();
  506. pool_map->set("excluded-prefix-len",
  507. Element::create(static_cast<int>(xlen)));
  508. }
  509. // Set user-context
  510. ConstElementPtr context = pdpool->getContext();
  511. if (!isNull(context)) {
  512. pool_map->set("user-context", context);
  513. }
  514. // Set pool options
  515. ConstCfgOptionPtr opts = pdpool->getCfgOption();
  516. pool_map->set("option-data", opts->toElement());
  517. // Push on the pool list
  518. pdpool_list->add(pool_map);
  519. }
  520. map->set("pd-pools", pdpool_list);
  521. return (map);
  522. }
  523. } // end of isc::dhcp namespace
  524. } // end of isc namespace