cfg_subnets4.cc 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. // Copyright (C) 2014-2015 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. if (selector.dhcp4o6_) {
  66. return selectSubnet4o6(selector);
  67. }
  68. // First use RAI link select sub-option or subnet select option
  69. if (!selector.option_select_.isV4Zero()) {
  70. return (selectSubnet(selector.option_select_,
  71. selector.client_classes_));
  72. }
  73. // If relayed message has been received, try to match the giaddr with the
  74. // relay address specified for a subnet. It is also possible that the relay
  75. // address will not match with any of the relay addresses accross all
  76. // subnets, but we need to verify that for all subnets before we can try
  77. // to use the giaddr to match with the subnet prefix.
  78. if (!selector.giaddr_.isV4Zero()) {
  79. for (Subnet4Collection::const_iterator subnet = subnets_.begin();
  80. subnet != subnets_.end(); ++subnet) {
  81. // Check if the giaddr is equal to the one defined for the subnet.
  82. if (selector.giaddr_ != (*subnet)->getRelayInfo().addr_) {
  83. continue;
  84. }
  85. // Eliminate those subnets that do not meet client class criteria.
  86. if ((*subnet)->clientSupported(selector.client_classes_)) {
  87. return (*subnet);
  88. }
  89. }
  90. }
  91. // If we got to this point it means that we were not able to match the
  92. // giaddr with any of the addresses specified for subnets. Let's determine
  93. // what address from the client's packet to use to match with the
  94. // subnets' prefixes.
  95. IOAddress address = IOAddress::IPV4_ZERO_ADDRESS();
  96. // If there is a giaddr, use it for subnet selection.
  97. if (!selector.giaddr_.isV4Zero()) {
  98. address = selector.giaddr_;
  99. // If it is a Renew or Rebind, use the ciaddr.
  100. } else if (!selector.ciaddr_.isV4Zero() &&
  101. !selector.local_address_.isV4Bcast()) {
  102. address = selector.ciaddr_;
  103. // If ciaddr is not specified, use the source address.
  104. } else if (!selector.remote_address_.isV4Zero() &&
  105. !selector.local_address_.isV4Bcast()) {
  106. address = selector.remote_address_;
  107. // If local interface name is known, use the local address on this
  108. // interface.
  109. } else if (!selector.iface_name_.empty()) {
  110. IfacePtr iface = IfaceMgr::instance().getIface(selector.iface_name_);
  111. // This should never happen in the real life. Hence we throw an
  112. // exception.
  113. if (iface == NULL) {
  114. isc_throw(isc::BadValue, "interface " << selector.iface_name_
  115. << " doesn't exist and therefore it is impossible"
  116. " to find a suitable subnet for its IPv4 address");
  117. }
  118. // Attempt to select subnet based on the interface name.
  119. Subnet4Ptr subnet = selectSubnet(selector.iface_name_,
  120. selector.client_classes_);
  121. // If it matches - great. If not, we'll try to use a different
  122. // selection criteria below.
  123. if (subnet) {
  124. return (subnet);
  125. } else {
  126. // Let's try to get an address from the local interface and
  127. // try to match it to defined subnet.
  128. iface->getAddress4(address);
  129. }
  130. }
  131. // Unable to find a suitable address to use for subnet selection.
  132. if (address.isV4Zero()) {
  133. return (Subnet4Ptr());
  134. }
  135. // We have identified an address in the client's packet that can be
  136. // used for subnet selection. Match this packet with the subnets.
  137. return (selectSubnet(address, selector.client_classes_));
  138. }
  139. Subnet4Ptr
  140. CfgSubnets4::selectSubnet(const std::string& iface,
  141. const ClientClasses& client_classes) const {
  142. for (Subnet4Collection::const_iterator subnet = subnets_.begin();
  143. subnet != subnets_.end(); ++subnet) {
  144. // If there's no interface specified for this subnet, proceed to
  145. // the next subnet.
  146. if ((*subnet)->getIface().empty()) {
  147. continue;
  148. }
  149. // If it's specified, but does not match, proceed to the next
  150. // subnet.
  151. if ((*subnet)->getIface() != iface) {
  152. continue;
  153. }
  154. // Eliminate those subnets that do not meet client class criteria.
  155. if ((*subnet)->clientSupported(client_classes)) {
  156. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
  157. DHCPSRV_CFGMGR_SUBNET4_IFACE)
  158. .arg((*subnet)->toText()).arg(iface);
  159. return (*subnet);
  160. }
  161. }
  162. // Failed to find a subnet.
  163. return (Subnet4Ptr());
  164. }
  165. Subnet4Ptr
  166. CfgSubnets4::selectSubnet(const IOAddress& address,
  167. const ClientClasses& client_classes) const {
  168. for (Subnet4Collection::const_iterator subnet = subnets_.begin();
  169. subnet != subnets_.end(); ++subnet) {
  170. // Address is in range for the subnet prefix, so return it.
  171. if (!(*subnet)->inRange(address)) {
  172. continue;
  173. }
  174. // Eliminate those subnets that do not meet client class criteria.
  175. if ((*subnet)->clientSupported(client_classes)) {
  176. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_SUBNET4_ADDR)
  177. .arg((*subnet)->toText()).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. /// @todo: May move this to CfgSubnets4 class if there will be more
  199. /// statistics here.
  200. for (Subnet4Collection::const_iterator subnet4 = subnets_.begin();
  201. subnet4 != subnets_.end(); ++subnet4) {
  202. StatsMgr::instance().del(StatsMgr::generateName("subnet",
  203. (*subnet4)->getID(),
  204. "total-addresses"));
  205. StatsMgr::instance().del(StatsMgr::generateName("subnet",
  206. (*subnet4)->getID(),
  207. "assigned-addresses"));
  208. }
  209. }
  210. void
  211. CfgSubnets4::updateStatistics() {
  212. using namespace isc::stats;
  213. /// @todo: May move this to CfgSubnets4 class if there will be more
  214. /// statistics here.
  215. for (Subnet4Collection::const_iterator subnet = subnets_.begin();
  216. subnet != subnets_.end(); ++subnet) {
  217. StatsMgr::instance().setValue(
  218. StatsMgr::generateName("subnet", (*subnet)->getID(), "total-addresses"),
  219. static_cast<int64_t>((*subnet)->getPoolCapacity(Lease::TYPE_V4)));
  220. }
  221. }
  222. } // end of namespace isc::dhcp
  223. } // end of namespace isc