cfg_subnets4.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. // Copyright (C) 2014-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 <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/shared_network.h>
  12. #include <dhcpsrv/subnet_id.h>
  13. #include <dhcpsrv/addr_utilities.h>
  14. #include <asiolink/io_address.h>
  15. #include <stats/stats_mgr.h>
  16. #include <sstream>
  17. using namespace isc::asiolink;
  18. using namespace isc::data;
  19. namespace isc {
  20. namespace dhcp {
  21. void
  22. CfgSubnets4::add(const Subnet4Ptr& subnet) {
  23. if (getBySubnetId(subnet->getID())) {
  24. isc_throw(isc::dhcp::DuplicateSubnetID, "ID of the new IPv4 subnet '"
  25. << subnet->getID() << "' is already in use");
  26. } else if (getByPrefix(subnet->toText())) {
  27. /// @todo: Check that this new subnet does not cross boundaries of any
  28. /// other already defined subnet.
  29. isc_throw(isc::dhcp::DuplicateSubnetID, "subnet with the prefix of '"
  30. << subnet->toText() << "' already exists");
  31. }
  32. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_ADD_SUBNET4)
  33. .arg(subnet->toText());
  34. subnets_.push_back(subnet);
  35. }
  36. void
  37. CfgSubnets4::del(const ConstSubnet4Ptr& subnet) {
  38. auto& index = subnets_.get<SubnetSubnetIdIndexTag>();
  39. auto subnet_it = index.find(subnet->getID());
  40. if (subnet_it == index.end()) {
  41. isc_throw(BadValue, "no subnet with ID of '" << subnet->getID()
  42. << "' found");
  43. }
  44. index.erase(subnet_it);
  45. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_DEL_SUBNET4)
  46. .arg(subnet->toText());
  47. }
  48. ConstSubnet4Ptr
  49. CfgSubnets4::getBySubnetId(const SubnetID& subnet_id) const {
  50. const auto& index = subnets_.get<SubnetSubnetIdIndexTag>();
  51. auto subnet_it = index.find(subnet_id);
  52. return ((subnet_it != index.cend()) ? (*subnet_it) : ConstSubnet4Ptr());
  53. }
  54. ConstSubnet4Ptr
  55. CfgSubnets4::getByPrefix(const std::string& subnet_text) const {
  56. const auto& index = subnets_.get<SubnetPrefixIndexTag>();
  57. auto subnet_it = index.find(subnet_text);
  58. return ((subnet_it != index.cend()) ? (*subnet_it) : ConstSubnet4Ptr());
  59. }
  60. Subnet4Ptr
  61. CfgSubnets4::selectSubnet4o6(const SubnetSelector& selector) const {
  62. for (Subnet4Collection::const_iterator subnet = subnets_.begin();
  63. subnet != subnets_.end(); ++subnet) {
  64. Cfg4o6& cfg4o6 = (*subnet)->get4o6();
  65. // Is this an 4o6 subnet at all?
  66. if (!cfg4o6.enabled()) {
  67. continue; // No? Let's try the next one.
  68. }
  69. // First match criteria: check if we have a prefix/len defined.
  70. std::pair<asiolink::IOAddress, uint8_t> pref = cfg4o6.getSubnet4o6();
  71. if (!pref.first.isV6Zero()) {
  72. // Let's check if the IPv6 address is in range
  73. IOAddress first = firstAddrInPrefix(pref.first, pref.second);
  74. IOAddress last = lastAddrInPrefix(pref.first, pref.second);
  75. if ((first <= selector.remote_address_) &&
  76. (selector.remote_address_ <= last)) {
  77. return (*subnet);
  78. }
  79. }
  80. // Second match criteria: check if the interface-id matches
  81. if (cfg4o6.getInterfaceId() && selector.interface_id_ &&
  82. cfg4o6.getInterfaceId()->equals(selector.interface_id_)) {
  83. return (*subnet);
  84. }
  85. // Third match criteria: check if the interface name matches
  86. if (!cfg4o6.getIface4o6().empty() && !selector.iface_name_.empty()
  87. && cfg4o6.getIface4o6() == selector.iface_name_) {
  88. return (*subnet);
  89. }
  90. }
  91. // Ok, wasn't able to find any matching subnet.
  92. return (Subnet4Ptr());
  93. }
  94. Subnet4Ptr
  95. CfgSubnets4::selectSubnet(const SubnetSelector& selector) const {
  96. // First use RAI link select sub-option or subnet select option
  97. if (!selector.option_select_.isV4Zero()) {
  98. return (selectSubnet(selector.option_select_,
  99. selector.client_classes_));
  100. }
  101. // If relayed message has been received, try to match the giaddr with the
  102. // relay address specified for a subnet and/or shared network. It is also
  103. // possible that the relay address will not match with any of the relay
  104. // addresses across all subnets, but we need to verify that for all subnets
  105. // before we can try to use the giaddr to match with the subnet prefix.
  106. if (!selector.giaddr_.isV4Zero()) {
  107. for (Subnet4Collection::const_iterator subnet = subnets_.begin();
  108. subnet != subnets_.end(); ++subnet) {
  109. // If relay information is specified for this subnet, it must match.
  110. // Otherwise, we ignore this subnet.
  111. if (!(*subnet)->getRelayInfo().addr_.isV4Zero()) {
  112. if (selector.giaddr_ != (*subnet)->getRelayInfo().addr_) {
  113. continue;
  114. }
  115. } else {
  116. // Relay information is not specified on the subnet level,
  117. // so let's try matching on the shared network level.
  118. SharedNetwork4Ptr network;
  119. (*subnet)->getSharedNetwork(network);
  120. if (!network || (selector.giaddr_ != network->getRelayInfo().addr_)) {
  121. continue;
  122. }
  123. }
  124. // If a subnet meets the client class criteria return it.
  125. if ((*subnet)->clientSupported(selector.client_classes_)) {
  126. return (*subnet);
  127. }
  128. }
  129. }
  130. // If we got to this point it means that we were not able to match the
  131. // giaddr with any of the addresses specified for subnets. Let's determine
  132. // what address from the client's packet to use to match with the
  133. // subnets' prefixes.
  134. IOAddress address = IOAddress::IPV4_ZERO_ADDRESS();
  135. // If there is a giaddr, use it for subnet selection.
  136. if (!selector.giaddr_.isV4Zero()) {
  137. address = selector.giaddr_;
  138. // If it is a Renew or Rebind, use the ciaddr.
  139. } else if (!selector.ciaddr_.isV4Zero() &&
  140. !selector.local_address_.isV4Bcast()) {
  141. address = selector.ciaddr_;
  142. // If ciaddr is not specified, use the source address.
  143. } else if (!selector.remote_address_.isV4Zero() &&
  144. !selector.local_address_.isV4Bcast()) {
  145. address = selector.remote_address_;
  146. // If local interface name is known, use the local address on this
  147. // interface.
  148. } else if (!selector.iface_name_.empty()) {
  149. IfacePtr iface = IfaceMgr::instance().getIface(selector.iface_name_);
  150. // This should never happen in the real life. Hence we throw an
  151. // exception.
  152. if (iface == NULL) {
  153. isc_throw(isc::BadValue, "interface " << selector.iface_name_
  154. << " doesn't exist and therefore it is impossible"
  155. " to find a suitable subnet for its IPv4 address");
  156. }
  157. // Attempt to select subnet based on the interface name.
  158. Subnet4Ptr subnet = selectSubnet(selector.iface_name_,
  159. selector.client_classes_);
  160. // If it matches - great. If not, we'll try to use a different
  161. // selection criteria below.
  162. if (subnet) {
  163. return (subnet);
  164. } else {
  165. // Let's try to get an address from the local interface and
  166. // try to match it to defined subnet.
  167. iface->getAddress4(address);
  168. }
  169. }
  170. // Unable to find a suitable address to use for subnet selection.
  171. if (address.isV4Zero()) {
  172. return (Subnet4Ptr());
  173. }
  174. // We have identified an address in the client's packet that can be
  175. // used for subnet selection. Match this packet with the subnets.
  176. return (selectSubnet(address, selector.client_classes_));
  177. }
  178. Subnet4Ptr
  179. CfgSubnets4::selectSubnet(const std::string& iface,
  180. const ClientClasses& client_classes) const {
  181. for (Subnet4Collection::const_iterator subnet = subnets_.begin();
  182. subnet != subnets_.end(); ++subnet) {
  183. Subnet4Ptr subnet_selected;
  184. // First, try subnet specific interface name.
  185. if (!(*subnet)->getIface().empty()) {
  186. if ((*subnet)->getIface() == iface) {
  187. subnet_selected = (*subnet);
  188. }
  189. } else {
  190. // Interface not specified for a subnet, so let's try if
  191. // we can match with shared network specific setting of
  192. // the interface.
  193. SharedNetwork4Ptr network;
  194. (*subnet)->getSharedNetwork(network);
  195. if (network && !network->getIface().empty() &&
  196. (network->getIface() == iface)) {
  197. subnet_selected = (*subnet);
  198. }
  199. }
  200. if (subnet_selected) {
  201. // If a subnet meets the client class criteria return it.
  202. if (subnet_selected->clientSupported(client_classes)) {
  203. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
  204. DHCPSRV_CFGMGR_SUBNET4_IFACE)
  205. .arg((*subnet)->toText())
  206. .arg(iface);
  207. return (subnet_selected);
  208. }
  209. }
  210. }
  211. // Failed to find a subnet.
  212. return (Subnet4Ptr());
  213. }
  214. Subnet4Ptr
  215. CfgSubnets4::getSubnet(const SubnetID id) const {
  216. /// @todo: Once this code is migrated to multi-index container, use
  217. /// an index rather than full scan.
  218. for (auto subnet = subnets_.begin(); subnet != subnets_.end(); ++subnet) {
  219. if ((*subnet)->getID() == id) {
  220. return (*subnet);
  221. }
  222. }
  223. return (Subnet4Ptr());
  224. }
  225. Subnet4Ptr
  226. CfgSubnets4::selectSubnet(const IOAddress& address,
  227. const ClientClasses& client_classes) const {
  228. for (Subnet4Collection::const_iterator subnet = subnets_.begin();
  229. subnet != subnets_.end(); ++subnet) {
  230. // Address is in range for the subnet prefix, so return it.
  231. if (!(*subnet)->inRange(address)) {
  232. continue;
  233. }
  234. // If a subnet meets the client class criteria return it.
  235. if ((*subnet)->clientSupported(client_classes)) {
  236. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_SUBNET4_ADDR)
  237. .arg((*subnet)->toText())
  238. .arg(address.toText());
  239. return (*subnet);
  240. }
  241. }
  242. // Failed to find a subnet.
  243. return (Subnet4Ptr());
  244. }
  245. void
  246. CfgSubnets4::removeStatistics() {
  247. using namespace isc::stats;
  248. // For each v4 subnet currently configured, remove the statistic.
  249. StatsMgr& stats_mgr = StatsMgr::instance();
  250. for (Subnet4Collection::const_iterator subnet4 = subnets_.begin();
  251. subnet4 != subnets_.end(); ++subnet4) {
  252. SubnetID subnet_id = (*subnet4)->getID();
  253. stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
  254. "total-addresses"));
  255. stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
  256. "assigned-addresses"));
  257. stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
  258. "declined-addresses"));
  259. stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
  260. "declined-reclaimed-addresses"));
  261. stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
  262. "reclaimed-leases"));
  263. }
  264. }
  265. void
  266. CfgSubnets4::updateStatistics() {
  267. using namespace isc::stats;
  268. StatsMgr& stats_mgr = StatsMgr::instance();
  269. for (Subnet4Collection::const_iterator subnet4 = subnets_.begin();
  270. subnet4 != subnets_.end(); ++subnet4) {
  271. SubnetID subnet_id = (*subnet4)->getID();
  272. stats_mgr.setValue(StatsMgr::
  273. generateName("subnet", subnet_id, "total-addresses"),
  274. static_cast<int64_t>
  275. ((*subnet4)->getPoolCapacity(Lease::
  276. TYPE_V4)));
  277. }
  278. // Only recount the stats if we have subnets.
  279. if (subnets_.begin() != subnets_.end()) {
  280. LeaseMgrFactory::instance().recountLeaseStats4();
  281. }
  282. }
  283. ElementPtr
  284. CfgSubnets4::toElement() const {
  285. ElementPtr result = Element::createList();
  286. // Iterate subnets
  287. for (Subnet4Collection::const_iterator subnet = subnets_.cbegin();
  288. subnet != subnets_.cend(); ++subnet) {
  289. result->add((*subnet)->toElement());
  290. }
  291. return (result);
  292. }
  293. } // end of namespace isc::dhcp
  294. } // end of namespace isc