subnet.cc 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. // Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #include <asiolink/io_address.h>
  15. #include <dhcpsrv/addr_utilities.h>
  16. #include <dhcpsrv/subnet.h>
  17. #include <sstream>
  18. using namespace isc::asiolink;
  19. namespace isc {
  20. namespace dhcp {
  21. Subnet::Subnet(const isc::asiolink::IOAddress& prefix, uint8_t len,
  22. const Triplet<uint32_t>& t1,
  23. const Triplet<uint32_t>& t2,
  24. const Triplet<uint32_t>& valid_lifetime)
  25. :id_(getNextID()), prefix_(prefix), prefix_len_(len), t1_(t1),
  26. t2_(t2), valid_(valid_lifetime),
  27. last_allocated_(lastAddrInPrefix(prefix, len)) {
  28. if ((prefix.isV6() && len > 128) ||
  29. (prefix.isV4() && len > 32)) {
  30. isc_throw(BadValue, "Invalid prefix length specified for subnet: " << len);
  31. }
  32. }
  33. bool Subnet::inRange(const isc::asiolink::IOAddress& addr) const {
  34. IOAddress first = firstAddrInPrefix(prefix_, prefix_len_);
  35. IOAddress last = lastAddrInPrefix(prefix_, prefix_len_);
  36. return ((first <= addr) && (addr <= last));
  37. }
  38. void
  39. Subnet::addOption(OptionPtr& option, bool persistent,
  40. const std::string& option_space) {
  41. // @todo Once the #2313 is merged we need to use the OptionSpace object to
  42. // validate the option space name here. For now, let's check that the name
  43. // is not empty as the empty namespace has a special meaning here - it is
  44. // returned when desired namespace is not found when getOptions is called.
  45. if (option_space.empty()) {
  46. isc_throw(isc::BadValue, "option space name must not be empty");
  47. }
  48. validateOption(option);
  49. option_spaces_[option_space].push_back(OptionDescriptor(option, persistent));
  50. }
  51. void
  52. Subnet::delOptions() {
  53. option_spaces_.clear();
  54. }
  55. const Subnet::OptionContainer&
  56. Subnet::emptyOptionContainer() {
  57. static OptionContainer container;
  58. return (container);
  59. }
  60. const Subnet::OptionContainer&
  61. Subnet::getOptions(const std::string& option_space) const {
  62. // Search the map to get the options container for the particular
  63. // option space.
  64. const std::map<std::string, OptionContainer>::const_iterator& options =
  65. option_spaces_.find(option_space);
  66. // If the option space has not been found it means that no option
  67. // has been configured for this option space yet. Thus we have to
  68. // return an empty container to the caller.
  69. if (options == option_spaces_.end()) {
  70. return (emptyOptionContainer());
  71. }
  72. // We found some option container for the option space specified.
  73. // Let's return a const reference to it.
  74. return (options->second);
  75. }
  76. Subnet::OptionDescriptor
  77. Subnet::getOptionSingle(const std::string& option_space,
  78. const uint16_t option_code) {
  79. const OptionContainer& options = getOptions(option_space);
  80. if (options.empty()) {
  81. return (OptionDescriptor(false));
  82. }
  83. const OptionContainerTypeIndex& idx = options.get<1>();
  84. const OptionContainerTypeRange& range = idx.equal_range(option_code);
  85. if (std::distance(range.first, range.second) == 0) {
  86. return (OptionDescriptor(false));
  87. }
  88. return (*range.first);
  89. }
  90. std::string Subnet::toText() const {
  91. std::stringstream tmp;
  92. tmp << prefix_.toText() << "/" << static_cast<unsigned int>(prefix_len_);
  93. return (tmp.str());
  94. }
  95. Subnet4::Subnet4(const isc::asiolink::IOAddress& prefix, uint8_t length,
  96. const Triplet<uint32_t>& t1,
  97. const Triplet<uint32_t>& t2,
  98. const Triplet<uint32_t>& valid_lifetime)
  99. :Subnet(prefix, length, t1, t2, valid_lifetime) {
  100. if (!prefix.isV4()) {
  101. isc_throw(BadValue, "Non IPv4 prefix " << prefix.toText()
  102. << " specified in subnet4");
  103. }
  104. }
  105. void Subnet4::addPool4(const Pool4Ptr& pool) {
  106. IOAddress first_addr = pool->getFirstAddress();
  107. IOAddress last_addr = pool->getLastAddress();
  108. if (!inRange(first_addr) || !inRange(last_addr)) {
  109. isc_throw(BadValue, "Pool4 (" << first_addr.toText() << "-" << last_addr.toText()
  110. << " does not belong in this (" << prefix_ << "/" << prefix_len_
  111. << ") subnet4");
  112. }
  113. /// @todo: Check that pools do not overlap
  114. pools_.push_back(pool);
  115. }
  116. Pool4Ptr Subnet4::getPool4(const isc::asiolink::IOAddress& hint /* = IOAddress("::")*/ ) {
  117. Pool4Ptr candidate;
  118. for (Pool4Collection::iterator pool = pools_.begin(); pool != pools_.end(); ++pool) {
  119. // if we won't find anything better, then let's just use the first pool
  120. if (!candidate) {
  121. candidate = *pool;
  122. }
  123. // if the client provided a pool and there's a pool that hint is valid in,
  124. // then let's use that pool
  125. if ((*pool)->inRange(hint)) {
  126. return (*pool);
  127. }
  128. }
  129. return (candidate);
  130. }
  131. void
  132. Subnet4::validateOption(const OptionPtr& option) const {
  133. if (!option) {
  134. isc_throw(isc::BadValue, "option configured for subnet must not be NULL");
  135. } else if (option->getUniverse() != Option::V4) {
  136. isc_throw(isc::BadValue, "expected V4 option to be added to the subnet");
  137. }
  138. }
  139. bool Subnet4::inPool(const isc::asiolink::IOAddress& addr) const {
  140. // Let's start with checking if it even belongs to that subnet.
  141. if (!inRange(addr)) {
  142. return (false);
  143. }
  144. for (Pool4Collection::const_iterator pool = pools_.begin(); pool != pools_.end(); ++pool) {
  145. if ((*pool)->inRange(addr)) {
  146. return (true);
  147. }
  148. }
  149. // there's no pool that address belongs to
  150. return (false);
  151. }
  152. Subnet6::Subnet6(const isc::asiolink::IOAddress& prefix, uint8_t length,
  153. const Triplet<uint32_t>& t1,
  154. const Triplet<uint32_t>& t2,
  155. const Triplet<uint32_t>& preferred_lifetime,
  156. const Triplet<uint32_t>& valid_lifetime)
  157. :Subnet(prefix, length, t1, t2, valid_lifetime),
  158. preferred_(preferred_lifetime){
  159. if (!prefix.isV6()) {
  160. isc_throw(BadValue, "Non IPv6 prefix " << prefix.toText()
  161. << " specified in subnet6");
  162. }
  163. }
  164. void Subnet6::addPool6(const Pool6Ptr& pool) {
  165. IOAddress first_addr = pool->getFirstAddress();
  166. IOAddress last_addr = pool->getLastAddress();
  167. if (!inRange(first_addr) || !inRange(last_addr)) {
  168. isc_throw(BadValue, "Pool6 (" << first_addr.toText() << "-" << last_addr.toText()
  169. << " does not belong in this (" << prefix_ << "/" << prefix_len_
  170. << ") subnet6");
  171. }
  172. /// @todo: Check that pools do not overlap
  173. pools_.push_back(pool);
  174. }
  175. Pool6Ptr Subnet6::getPool6(const isc::asiolink::IOAddress& hint /* = IOAddress("::")*/ ) {
  176. Pool6Ptr candidate;
  177. for (Pool6Collection::iterator pool = pools_.begin(); pool != pools_.end(); ++pool) {
  178. // if we won't find anything better, then let's just use the first pool
  179. if (!candidate) {
  180. candidate = *pool;
  181. }
  182. // if the client provided a pool and there's a pool that hint is valid in,
  183. // then let's use that pool
  184. if ((*pool)->inRange(hint)) {
  185. return (*pool);
  186. }
  187. }
  188. return (candidate);
  189. }
  190. void
  191. Subnet6::validateOption(const OptionPtr& option) const {
  192. if (!option) {
  193. isc_throw(isc::BadValue, "option configured for subnet must not be NULL");
  194. } else if (option->getUniverse() != Option::V6) {
  195. isc_throw(isc::BadValue, "expected V6 option to be added to the subnet");
  196. }
  197. }
  198. bool Subnet6::inPool(const isc::asiolink::IOAddress& addr) const {
  199. // Let's start with checking if it even belongs to that subnet.
  200. if (!inRange(addr)) {
  201. return (false);
  202. }
  203. for (Pool6Collection::const_iterator pool = pools_.begin(); pool != pools_.end(); ++pool) {
  204. if ((*pool)->inRange(addr)) {
  205. return (true);
  206. }
  207. }
  208. // there's no pool that address belongs to
  209. return (false);
  210. }
  211. } // end of isc::dhcp namespace
  212. } // end of isc namespace