cfg_subnets4.cc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  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/subnet_id.h>
  12. #include <dhcpsrv/addr_utilities.h>
  13. #include <asiolink/io_address.h>
  14. #include <stats/stats_mgr.h>
  15. #include <sstream>
  16. using namespace isc::asiolink;
  17. using namespace isc::data;
  18. namespace isc {
  19. namespace dhcp {
  20. void
  21. CfgSubnets4::add(const Subnet4Ptr& subnet) {
  22. /// @todo: Check that this new subnet does not cross boundaries of any
  23. /// other already defined subnet.
  24. if (isDuplicate(*subnet)) {
  25. isc_throw(isc::dhcp::DuplicateSubnetID, "ID of the new IPv4 subnet '"
  26. << subnet->getID() << "' is already in use");
  27. }
  28. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_ADD_SUBNET4)
  29. .arg(subnet->toText());
  30. subnets_.push_back(subnet);
  31. }
  32. Subnet4Ptr
  33. CfgSubnets4::selectSubnet4o6(const SubnetSelector& selector) const {
  34. for (Subnet4Collection::const_iterator subnet = subnets_.begin();
  35. subnet != subnets_.end(); ++subnet) {
  36. Cfg4o6& cfg4o6 = (*subnet)->get4o6();
  37. // Is this an 4o6 subnet at all?
  38. if (!cfg4o6.enabled()) {
  39. continue; // No? Let's try the next one.
  40. }
  41. // First match criteria: check if we have a prefix/len defined.
  42. std::pair<asiolink::IOAddress, uint8_t> pref = cfg4o6.getSubnet4o6();
  43. if (!pref.first.isV6Zero()) {
  44. // Let's check if the IPv6 address is in range
  45. IOAddress first = firstAddrInPrefix(pref.first, pref.second);
  46. IOAddress last = lastAddrInPrefix(pref.first, pref.second);
  47. if ((first <= selector.remote_address_) &&
  48. (selector.remote_address_ <= last)) {
  49. return (*subnet);
  50. }
  51. }
  52. // Second match criteria: check if the interface-id matches
  53. if (cfg4o6.getInterfaceId() && selector.interface_id_ &&
  54. cfg4o6.getInterfaceId()->equals(selector.interface_id_)) {
  55. return (*subnet);
  56. }
  57. // Third match criteria: check if the interface name matches
  58. if (!cfg4o6.getIface4o6().empty() && !selector.iface_name_.empty()
  59. && cfg4o6.getIface4o6() == selector.iface_name_) {
  60. return (*subnet);
  61. }
  62. }
  63. // Ok, wasn't able to find any matching subnet.
  64. return (Subnet4Ptr());
  65. }
  66. Subnet4Ptr
  67. CfgSubnets4::selectSubnet(const SubnetSelector& selector) const {
  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 across 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. // If a subnet meets the client class criteria return it.
  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. // If a subnet meets the client class criteria return it.
  155. if ((*subnet)->clientSupported(client_classes)) {
  156. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
  157. DHCPSRV_CFGMGR_SUBNET4_IFACE)
  158. .arg((*subnet)->toText())
  159. .arg(iface);
  160. return (*subnet);
  161. }
  162. }
  163. // Failed to find a subnet.
  164. return (Subnet4Ptr());
  165. }
  166. Subnet4Ptr
  167. CfgSubnets4::getSubnet(const SubnetID id) const {
  168. /// @todo: Once this code is migrated to multi-index container, use
  169. /// an index rather than full scan.
  170. for (auto subnet = subnets_.begin(); subnet != subnets_.end(); ++subnet) {
  171. if ((*subnet)->getID() == id) {
  172. return (*subnet);
  173. }
  174. }
  175. return (Subnet4Ptr());
  176. }
  177. Subnet4Ptr
  178. CfgSubnets4::selectSubnet(const IOAddress& address,
  179. const ClientClasses& client_classes) const {
  180. for (Subnet4Collection::const_iterator subnet = subnets_.begin();
  181. subnet != subnets_.end(); ++subnet) {
  182. // Address is in range for the subnet prefix, so return it.
  183. if (!(*subnet)->inRange(address)) {
  184. continue;
  185. }
  186. // If a subnet meets the client class criteria return it.
  187. if ((*subnet)->clientSupported(client_classes)) {
  188. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_SUBNET4_ADDR)
  189. .arg((*subnet)->toText())
  190. .arg(address.toText());
  191. return (*subnet);
  192. }
  193. }
  194. // Failed to find a subnet.
  195. return (Subnet4Ptr());
  196. }
  197. bool
  198. CfgSubnets4::isDuplicate(const Subnet4& subnet) const {
  199. for (Subnet4Collection::const_iterator subnet_it = subnets_.begin();
  200. subnet_it != subnets_.end(); ++subnet_it) {
  201. if ((*subnet_it)->getID() == subnet.getID()) {
  202. return (true);
  203. }
  204. }
  205. return (false);
  206. }
  207. void
  208. CfgSubnets4::removeStatistics() {
  209. using namespace isc::stats;
  210. // For each v4 subnet currently configured, remove the statistic.
  211. StatsMgr& stats_mgr = StatsMgr::instance();
  212. for (Subnet4Collection::const_iterator subnet4 = subnets_.begin();
  213. subnet4 != subnets_.end(); ++subnet4) {
  214. SubnetID subnet_id = (*subnet4)->getID();
  215. stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
  216. "total-addresses"));
  217. stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
  218. "assigned-addresses"));
  219. stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
  220. "declined-addresses"));
  221. stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
  222. "declined-reclaimed-addresses"));
  223. stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
  224. "reclaimed-leases"));
  225. }
  226. }
  227. void
  228. CfgSubnets4::updateStatistics() {
  229. using namespace isc::stats;
  230. StatsMgr& stats_mgr = StatsMgr::instance();
  231. for (Subnet4Collection::const_iterator subnet4 = subnets_.begin();
  232. subnet4 != subnets_.end(); ++subnet4) {
  233. SubnetID subnet_id = (*subnet4)->getID();
  234. stats_mgr.setValue(StatsMgr::
  235. generateName("subnet", subnet_id, "total-addresses"),
  236. static_cast<int64_t>
  237. ((*subnet4)->getPoolCapacity(Lease::
  238. TYPE_V4)));
  239. }
  240. // Only recount the stats if we have subnets.
  241. if (subnets_.begin() != subnets_.end()) {
  242. LeaseMgrFactory::instance().recountLeaseStats4();
  243. }
  244. }
  245. ElementPtr
  246. CfgSubnets4::toElement() const {
  247. ElementPtr result = Element::createList();
  248. // Iterate subnets
  249. for (Subnet4Collection::const_iterator subnet = subnets_.cbegin();
  250. subnet != subnets_.cend(); ++subnet) {
  251. // Prepare the map
  252. ElementPtr map = Element::createMap();
  253. // Set subnet id
  254. SubnetID id = (*subnet)->getID();
  255. map->set("id", Element::create(static_cast<long long>(id)));
  256. // Set relay info
  257. const Subnet::RelayInfo& relay_info = (*subnet)->getRelayInfo();
  258. ElementPtr relay = Element::createMap();
  259. relay->set("ip-address", Element::create(relay_info.addr_.toText()));
  260. map->set("relay", relay);
  261. // Set subnet
  262. map->set("subnet", Element::create((*subnet)->toText()));
  263. // Set interface
  264. const std::string& iface = (*subnet)->getIface();
  265. map->set("interface", Element::create(iface));
  266. // Set renew-timer
  267. map->set("renew-timer",
  268. Element::create(static_cast<long long>
  269. ((*subnet)->getT1().get())));
  270. // Set rebind-timer
  271. map->set("rebind-timer",
  272. Element::create(static_cast<long long>
  273. ((*subnet)->getT2().get())));
  274. // Set valid-lifetime
  275. map->set("valid-lifetime",
  276. Element::create(static_cast<long long>
  277. ((*subnet)->getValid().get())));
  278. // Set pools
  279. const PoolCollection& pools = (*subnet)->getPools(Lease::TYPE_V4);
  280. ElementPtr pool_list = Element::createList();
  281. for (PoolCollection::const_iterator pool = pools.cbegin();
  282. pool != pools.cend(); ++pool) {
  283. // Prepare the map for a pool (@todo move this code to pool.cc)
  284. ElementPtr pool_map = Element::createMap();
  285. // Set pool
  286. const IOAddress& first = (*pool)->getFirstAddress();
  287. const IOAddress& last = (*pool)->getLastAddress();
  288. std::string range = first.toText() + "-" + last.toText();
  289. // Try to output a prefix (vs a range)
  290. int prefix_len = prefixLengthFromRange(first, last);
  291. if (prefix_len >= 0) {
  292. std::ostringstream oss;
  293. oss << first.toText() << "/" << prefix_len;
  294. range = oss.str();
  295. }
  296. pool_map->set("pool", Element::create(range));
  297. // Set user-context
  298. ConstElementPtr context = (*pool)->getContext();
  299. if (!isNull(context)) {
  300. pool_map->set("user-context", context);
  301. }
  302. // Set pool options
  303. ConstCfgOptionPtr opts = (*pool)->getCfgOption();
  304. pool_map->set("option-data", opts->toElement());
  305. // Push on the pool list
  306. pool_list->add(pool_map);
  307. }
  308. map->set("pools", pool_list);
  309. // Set host reservation-mode
  310. Subnet::HRMode hrmode = (*subnet)->getHostReservationMode();
  311. std::string mode;
  312. switch (hrmode) {
  313. case Subnet::HR_DISABLED:
  314. mode = "disabled";
  315. break;
  316. case Subnet::HR_OUT_OF_POOL:
  317. mode = "out-of-pool";
  318. break;
  319. case Subnet::HR_ALL:
  320. mode = "all";
  321. break;
  322. default:
  323. isc_throw(ToElementError,
  324. "invalid host reservation mode: " << hrmode);
  325. }
  326. map->set("reservation-mode", Element::create(mode));
  327. // Set match-client-id
  328. map->set("match-client-id",
  329. Element::create((*subnet)->getMatchClientId()));
  330. // Set next-server
  331. map->set("next-server",
  332. Element::create((*subnet)->getSiaddr().toText()));
  333. // Set DHCP4o6
  334. const Cfg4o6& d4o6 = (*subnet)->get4o6();
  335. isc::data::merge(map, d4o6.toElement());
  336. // Set client-class
  337. const ClientClasses& cclasses = (*subnet)->getClientClasses();
  338. if (cclasses.size() > 1) {
  339. isc_throw(ToElementError, "client-class has too many items: "
  340. << cclasses.size());
  341. } else if (!cclasses.empty()) {
  342. map->set("client-class", Element::create(*cclasses.cbegin()));
  343. }
  344. // Set options
  345. ConstCfgOptionPtr opts = (*subnet)->getCfgOption();
  346. map->set("option-data", opts->toElement());
  347. // Not supported: interface-id
  348. // Not supported: rapid-commit
  349. // Push on the list
  350. result->add(map);
  351. }
  352. return (result);
  353. }
  354. } // end of namespace isc::dhcp
  355. } // end of namespace isc