cfg_subnets4.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  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::selectSubnet(const IOAddress& address,
  168. const ClientClasses& client_classes) const {
  169. for (Subnet4Collection::const_iterator subnet = subnets_.begin();
  170. subnet != subnets_.end(); ++subnet) {
  171. // Address is in range for the subnet prefix, so return it.
  172. if (!(*subnet)->inRange(address)) {
  173. continue;
  174. }
  175. // If a subnet meets the client class criteria return it.
  176. if ((*subnet)->clientSupported(client_classes)) {
  177. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_SUBNET4_ADDR)
  178. .arg((*subnet)->toText())
  179. .arg(address.toText());
  180. return (*subnet);
  181. }
  182. }
  183. // Failed to find a subnet.
  184. return (Subnet4Ptr());
  185. }
  186. bool
  187. CfgSubnets4::isDuplicate(const Subnet4& subnet) const {
  188. for (Subnet4Collection::const_iterator subnet_it = subnets_.begin();
  189. subnet_it != subnets_.end(); ++subnet_it) {
  190. if ((*subnet_it)->getID() == subnet.getID()) {
  191. return (true);
  192. }
  193. }
  194. return (false);
  195. }
  196. void
  197. CfgSubnets4::removeStatistics() {
  198. using namespace isc::stats;
  199. // For each v4 subnet currently configured, remove the statistic.
  200. StatsMgr& stats_mgr = StatsMgr::instance();
  201. for (Subnet4Collection::const_iterator subnet4 = subnets_.begin();
  202. subnet4 != subnets_.end(); ++subnet4) {
  203. SubnetID subnet_id = (*subnet4)->getID();
  204. stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
  205. "total-addresses"));
  206. stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
  207. "assigned-addresses"));
  208. stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
  209. "declined-addresses"));
  210. stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
  211. "declined-reclaimed-addresses"));
  212. }
  213. }
  214. void
  215. CfgSubnets4::updateStatistics() {
  216. using namespace isc::stats;
  217. StatsMgr& stats_mgr = StatsMgr::instance();
  218. for (Subnet4Collection::const_iterator subnet4 = subnets_.begin();
  219. subnet4 != subnets_.end(); ++subnet4) {
  220. SubnetID subnet_id = (*subnet4)->getID();
  221. stats_mgr.setValue(StatsMgr::
  222. generateName("subnet", subnet_id, "total-addresses"),
  223. static_cast<int64_t>
  224. ((*subnet4)->getPoolCapacity(Lease::
  225. TYPE_V4)));
  226. }
  227. // Only recount the stats if we have subnets.
  228. if (subnets_.begin() != subnets_.end()) {
  229. LeaseMgrFactory::instance().recountLeaseStats4();
  230. }
  231. }
  232. ElementPtr
  233. CfgSubnets4::toElement() const {
  234. ElementPtr result = Element::createList();
  235. // Iterate subnets
  236. for (Subnet4Collection::const_iterator subnet = subnets_.cbegin();
  237. subnet != subnets_.cend(); ++subnet) {
  238. // Prepare the map
  239. ElementPtr map = Element::createMap();
  240. // Set subnet id
  241. SubnetID id = (*subnet)->getID();
  242. map->set("id", Element::create(static_cast<long long>(id)));
  243. // Set relay info
  244. const Subnet::RelayInfo& relay_info = (*subnet)->getRelayInfo();
  245. ElementPtr relay = Element::createMap();
  246. relay->set("ip-address", Element::create(relay_info.addr_.toText()));
  247. map->set("relay", relay);
  248. // Set subnet
  249. map->set("subnet", Element::create((*subnet)->toText()));
  250. // Set interface
  251. const std::string& iface = (*subnet)->getIface();
  252. map->set("interface", Element::create(iface));
  253. // Set renew-timer
  254. map->set("renew-timer",
  255. Element::create(static_cast<long long>
  256. ((*subnet)->getT1().get())));
  257. // Set rebind-timer
  258. map->set("rebind-timer",
  259. Element::create(static_cast<long long>
  260. ((*subnet)->getT2().get())));
  261. // Set valid-lifetime
  262. map->set("valid-lifetime",
  263. Element::create(static_cast<long long>
  264. ((*subnet)->getValid().get())));
  265. // Set pools
  266. const PoolCollection& pools = (*subnet)->getPools(Lease::TYPE_V4);
  267. ElementPtr pool_list = Element::createList();
  268. for (PoolCollection::const_iterator pool = pools.cbegin();
  269. pool != pools.cend(); ++pool) {
  270. // Prepare the map for a pool
  271. ElementPtr pool_map = Element::createMap();
  272. // Set pool
  273. const IOAddress& first = (*pool)->getFirstAddress();
  274. const IOAddress& last = (*pool)->getLastAddress();
  275. std::string range = first.toText() + "-" + last.toText();
  276. // Try to output a prefix (vs a range)
  277. int prefix_len = prefixLengthFromRange(first, last);
  278. if (prefix_len >= 0) {
  279. std::ostringstream oss;
  280. oss << first.toText() << "/" << prefix_len;
  281. range = oss.str();
  282. }
  283. pool_map->set("pool", Element::create(range));
  284. // Set user-context
  285. ConstElementPtr context = (*pool)->getContext();
  286. if (!isNull(context)) {
  287. pool_map->set("user-context", context);
  288. }
  289. // Set pool options
  290. ConstCfgOptionPtr opts = (*pool)->getCfgOption();
  291. pool_map->set("option-data", opts->toElement());
  292. // Push on the pool list
  293. pool_list->add(pool_map);
  294. }
  295. map->set("pools", pool_list);
  296. // Set host reservation-mode
  297. Subnet::HRMode hrmode = (*subnet)->getHostReservationMode();
  298. std::string mode;
  299. switch (hrmode) {
  300. case Subnet::HR_DISABLED:
  301. mode = "disabled";
  302. break;
  303. case Subnet::HR_OUT_OF_POOL:
  304. mode = "out-of-pool";
  305. break;
  306. case Subnet::HR_ALL:
  307. mode = "all";
  308. break;
  309. default:
  310. isc_throw(ToElementError,
  311. "invalid host reservation mode: " << hrmode);
  312. }
  313. map->set("reservation-mode", Element::create(mode));
  314. // Set match-client-id
  315. map->set("match-client-id",
  316. Element::create((*subnet)->getMatchClientId()));
  317. // Set next-server
  318. map->set("next-server",
  319. Element::create((*subnet)->getSiaddr().toText()));
  320. // Set DHCP4o6
  321. const Cfg4o6& d4o6 = (*subnet)->get4o6();
  322. merge(map, d4o6.toElement());
  323. // Set client-class
  324. const ClientClasses& cclasses = (*subnet)->getClientClasses();
  325. if (cclasses.size() > 1) {
  326. isc_throw(ToElementError, "client-classes has too many items: "
  327. << cclasses.size());
  328. } else if (!cclasses.empty()) {
  329. map->set("client-class", Element::create(*cclasses.cbegin()));
  330. }
  331. // Set options
  332. ConstCfgOptionPtr opts = (*subnet)->getCfgOption();
  333. map->set("option-data", opts->toElement());
  334. // Not supported: interface-id
  335. // Not supported: rapid-commit
  336. // Push on the list
  337. result->add(map);
  338. }
  339. return (result);
  340. }
  341. } // end of namespace isc::dhcp
  342. } // end of namespace isc