cfg_subnets4.cc 9.9 KB

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