cfg_subnets4.cc 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. // Copyright (C) 2014-2016 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 <dhcp/iface_mgr.h>
  8. #include <dhcpsrv/cfg_subnets4.h>
  9. #include <dhcpsrv/dhcpsrv_log.h>
  10. #include <dhcpsrv/subnet_id.h>
  11. #include <dhcpsrv/addr_utilities.h>
  12. #include <asiolink/io_address.h>
  13. #include <stats/stats_mgr.h>
  14. using namespace isc::asiolink;
  15. namespace isc {
  16. namespace dhcp {
  17. void
  18. CfgSubnets4::add(const Subnet4Ptr& subnet) {
  19. /// @todo: Check that this new subnet does not cross boundaries of any
  20. /// other already defined subnet.
  21. if (isDuplicate(*subnet)) {
  22. isc_throw(isc::dhcp::DuplicateSubnetID, "ID of the new IPv4 subnet '"
  23. << subnet->getID() << "' is already in use");
  24. }
  25. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_ADD_SUBNET4)
  26. .arg(subnet->toText());
  27. subnets_.push_back(subnet);
  28. }
  29. Subnet4Ptr
  30. CfgSubnets4::selectSubnet4o6(const SubnetSelector& selector) const {
  31. for (Subnet4Collection::const_iterator subnet = subnets_.begin();
  32. subnet != subnets_.end(); ++subnet) {
  33. Cfg4o6& cfg4o6 = (*subnet)->get4o6();
  34. // Is this an 4o6 subnet at all?
  35. if (!cfg4o6.enabled()) {
  36. continue; // No? Let's try the next one.
  37. }
  38. // First match criteria: check if we have a prefix/len defined.
  39. std::pair<asiolink::IOAddress, uint8_t> pref = cfg4o6.getSubnet4o6();
  40. if (!pref.first.isV6Zero()) {
  41. // Let's check if the IPv6 address is in range
  42. IOAddress first = firstAddrInPrefix(pref.first, pref.second);
  43. IOAddress last = lastAddrInPrefix(pref.first, pref.second);
  44. if ((first <= selector.remote_address_) &&
  45. (selector.remote_address_ <= last)) {
  46. return (*subnet);
  47. }
  48. }
  49. // Second match criteria: check if the interface-id matches
  50. if (cfg4o6.getInterfaceId() && selector.interface_id_ &&
  51. cfg4o6.getInterfaceId()->equals(selector.interface_id_)) {
  52. return (*subnet);
  53. }
  54. // Third match criteria: check if the interface name matches
  55. if (!cfg4o6.getIface4o6().empty() && !selector.iface_name_.empty()
  56. && cfg4o6.getIface4o6() == selector.iface_name_) {
  57. return (*subnet);
  58. }
  59. }
  60. // Ok, wasn't able to find any matching subnet.
  61. return (Subnet4Ptr());
  62. }
  63. Subnet4Ptr
  64. CfgSubnets4::selectSubnet(const SubnetSelector& selector) const {
  65. // First use RAI link select sub-option or subnet select option
  66. if (!selector.option_select_.isV4Zero()) {
  67. return (selectSubnet(selector.option_select_,
  68. selector.client_classes_));
  69. }
  70. // If relayed message has been received, try to match the giaddr with the
  71. // relay address specified for a subnet. It is also possible that the relay
  72. // address will not match with any of the relay addresses accross all
  73. // subnets, but we need to verify that for all subnets before we can try
  74. // to use the giaddr to match with the subnet prefix.
  75. if (!selector.giaddr_.isV4Zero()) {
  76. for (Subnet4Collection::const_iterator subnet = subnets_.begin();
  77. subnet != subnets_.end(); ++subnet) {
  78. // Check if the giaddr is equal to the one defined for the subnet.
  79. if (selector.giaddr_ != (*subnet)->getRelayInfo().addr_) {
  80. continue;
  81. }
  82. // If a subnet meets the client class criteria return it.
  83. if ((*subnet)->clientSupported(selector.client_classes_)) {
  84. return (*subnet);
  85. }
  86. }
  87. }
  88. // If we got to this point it means that we were not able to match the
  89. // giaddr with any of the addresses specified for subnets. Let's determine
  90. // what address from the client's packet to use to match with the
  91. // subnets' prefixes.
  92. IOAddress address = IOAddress::IPV4_ZERO_ADDRESS();
  93. // If there is a giaddr, use it for subnet selection.
  94. if (!selector.giaddr_.isV4Zero()) {
  95. address = selector.giaddr_;
  96. // If it is a Renew or Rebind, use the ciaddr.
  97. } else if (!selector.ciaddr_.isV4Zero() &&
  98. !selector.local_address_.isV4Bcast()) {
  99. address = selector.ciaddr_;
  100. // If ciaddr is not specified, use the source address.
  101. } else if (!selector.remote_address_.isV4Zero() &&
  102. !selector.local_address_.isV4Bcast()) {
  103. address = selector.remote_address_;
  104. // If local interface name is known, use the local address on this
  105. // interface.
  106. } else if (!selector.iface_name_.empty()) {
  107. IfacePtr iface = IfaceMgr::instance().getIface(selector.iface_name_);
  108. // This should never happen in the real life. Hence we throw an
  109. // exception.
  110. if (iface == NULL) {
  111. isc_throw(isc::BadValue, "interface " << selector.iface_name_
  112. << " doesn't exist and therefore it is impossible"
  113. " to find a suitable subnet for its IPv4 address");
  114. }
  115. // Attempt to select subnet based on the interface name.
  116. Subnet4Ptr subnet = selectSubnet(selector.iface_name_,
  117. selector.client_classes_);
  118. // If it matches - great. If not, we'll try to use a different
  119. // selection criteria below.
  120. if (subnet) {
  121. return (subnet);
  122. } else {
  123. // Let's try to get an address from the local interface and
  124. // try to match it to defined subnet.
  125. iface->getAddress4(address);
  126. }
  127. }
  128. // Unable to find a suitable address to use for subnet selection.
  129. if (address.isV4Zero()) {
  130. return (Subnet4Ptr());
  131. }
  132. // We have identified an address in the client's packet that can be
  133. // used for subnet selection. Match this packet with the subnets.
  134. return (selectSubnet(address, selector.client_classes_));
  135. }
  136. Subnet4Ptr
  137. CfgSubnets4::selectSubnet(const std::string& iface,
  138. const ClientClasses& client_classes) const {
  139. for (Subnet4Collection::const_iterator subnet = subnets_.begin();
  140. subnet != subnets_.end(); ++subnet) {
  141. // If there's no interface specified for this subnet, proceed to
  142. // the next subnet.
  143. if ((*subnet)->getIface().empty()) {
  144. continue;
  145. }
  146. // If it's specified, but does not match, proceed to the next
  147. // subnet.
  148. if ((*subnet)->getIface() != iface) {
  149. continue;
  150. }
  151. // If a subnet meets the client class criteria return it.
  152. if ((*subnet)->clientSupported(client_classes)) {
  153. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
  154. DHCPSRV_CFGMGR_SUBNET4_IFACE)
  155. .arg((*subnet)->toText())
  156. .arg(iface);
  157. return (*subnet);
  158. }
  159. }
  160. // Failed to find a subnet.
  161. return (Subnet4Ptr());
  162. }
  163. Subnet4Ptr
  164. CfgSubnets4::selectSubnet(const IOAddress& address,
  165. const ClientClasses& client_classes) const {
  166. for (Subnet4Collection::const_iterator subnet = subnets_.begin();
  167. subnet != subnets_.end(); ++subnet) {
  168. // Address is in range for the subnet prefix, so return it.
  169. if (!(*subnet)->inRange(address)) {
  170. continue;
  171. }
  172. // If a subnet meets the client class criteria return it.
  173. if ((*subnet)->clientSupported(client_classes)) {
  174. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_SUBNET4_ADDR)
  175. .arg((*subnet)->toText())
  176. .arg(address.toText());
  177. return (*subnet);
  178. }
  179. }
  180. // Failed to find a subnet.
  181. return (Subnet4Ptr());
  182. }
  183. bool
  184. CfgSubnets4::isDuplicate(const Subnet4& subnet) const {
  185. for (Subnet4Collection::const_iterator subnet_it = subnets_.begin();
  186. subnet_it != subnets_.end(); ++subnet_it) {
  187. if ((*subnet_it)->getID() == subnet.getID()) {
  188. return (true);
  189. }
  190. }
  191. return (false);
  192. }
  193. void
  194. CfgSubnets4::removeStatistics() {
  195. using namespace isc::stats;
  196. // For each v4 subnet currently configured, remove the statistic.
  197. /// @todo: May move this to CfgSubnets4 class if there will be more
  198. /// statistics here.
  199. for (Subnet4Collection::const_iterator subnet4 = subnets_.begin();
  200. subnet4 != subnets_.end(); ++subnet4) {
  201. StatsMgr::instance().del(StatsMgr::generateName("subnet",
  202. (*subnet4)->getID(),
  203. "total-addresses"));
  204. StatsMgr::instance().del(StatsMgr::generateName("subnet",
  205. (*subnet4)->getID(),
  206. "assigned-addresses"));
  207. }
  208. }
  209. void
  210. CfgSubnets4::updateStatistics() {
  211. using namespace isc::stats;
  212. /// @todo: May move this to CfgSubnets4 class if there will be more
  213. /// statistics here.
  214. for (Subnet4Collection::const_iterator subnet = subnets_.begin();
  215. subnet != subnets_.end(); ++subnet) {
  216. StatsMgr::instance().setValue(
  217. StatsMgr::generateName("subnet", (*subnet)->getID(), "total-addresses"),
  218. static_cast<int64_t>((*subnet)->getPoolCapacity(Lease::TYPE_V4)));
  219. }
  220. }
  221. } // end of namespace isc::dhcp
  222. } // end of namespace isc