cfg_subnets6.cc 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  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 <dhcpsrv/cfg_subnets6.h>
  8. #include <dhcpsrv/dhcpsrv_log.h>
  9. #include <dhcpsrv/lease_mgr_factory.h>
  10. #include <dhcpsrv/subnet_id.h>
  11. #include <dhcpsrv/addr_utilities.h>
  12. #include <stats/stats_mgr.h>
  13. #include <string.h>
  14. #include <sstream>
  15. using namespace isc::asiolink;
  16. using namespace isc::data;
  17. namespace isc {
  18. namespace dhcp {
  19. void
  20. CfgSubnets6::add(const Subnet6Ptr& subnet) {
  21. /// @todo: Check that this new subnet does not cross boundaries of any
  22. /// other already defined subnet.
  23. if (isDuplicate(*subnet)) {
  24. isc_throw(isc::dhcp::DuplicateSubnetID, "ID of the new IPv6 subnet '"
  25. << subnet->getID() << "' is already in use");
  26. }
  27. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_ADD_SUBNET6)
  28. .arg(subnet->toText());
  29. subnets_.push_back(subnet);
  30. }
  31. Subnet6Ptr
  32. CfgSubnets6::selectSubnet(const SubnetSelector& selector) const {
  33. Subnet6Ptr subnet;
  34. // If relay agent link address is set to zero it means that we're dealing
  35. // with a directly connected client.
  36. if (selector.first_relay_linkaddr_ == IOAddress("::")) {
  37. // If interface name is known try to match it with interface names
  38. // specified for configured subnets.
  39. if (!selector.iface_name_.empty()) {
  40. subnet = selectSubnet(selector.iface_name_,
  41. selector.client_classes_);
  42. }
  43. // If interface name didn't match, try the client's address.
  44. if (!subnet && selector.remote_address_ != IOAddress("::")) {
  45. subnet = selectSubnet(selector.remote_address_,
  46. selector.client_classes_);
  47. }
  48. // If relay agent link address is set, we're dealing with a relayed message.
  49. } else {
  50. // Find the subnet using the Interface Id option, if present.
  51. subnet = selectSubnet(selector.interface_id_, selector.client_classes_);
  52. // If Interface ID option could not be matched for any subnet, try
  53. // the relay agent link address.
  54. if (!subnet) {
  55. subnet = selectSubnet(selector.first_relay_linkaddr_,
  56. selector.client_classes_,
  57. true);
  58. }
  59. }
  60. // Return subnet found, or NULL if not found.
  61. return (subnet);
  62. }
  63. Subnet6Ptr
  64. CfgSubnets6::selectSubnet(const asiolink::IOAddress& address,
  65. const ClientClasses& client_classes,
  66. const bool is_relay_address) const {
  67. // If the specified address is a relay address we first need to match
  68. // it with the relay addresses specified for all subnets.
  69. if (is_relay_address) {
  70. for (Subnet6Collection::const_iterator subnet = subnets_.begin();
  71. subnet != subnets_.end(); ++subnet) {
  72. // If the specified address matches the relay address, return this
  73. // subnet.
  74. if (is_relay_address &&
  75. ((*subnet)->getRelayInfo().addr_ == address) &&
  76. (*subnet)->clientSupported(client_classes)) {
  77. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
  78. DHCPSRV_CFGMGR_SUBNET6_RELAY)
  79. .arg((*subnet)->toText()).arg(address.toText());
  80. return (*subnet);
  81. }
  82. }
  83. }
  84. // No success so far. Check if the specified address is in range
  85. // with any subnet.
  86. for (Subnet6Collection::const_iterator subnet = subnets_.begin();
  87. subnet != subnets_.end(); ++subnet) {
  88. if ((*subnet)->inRange(address) &&
  89. (*subnet)->clientSupported(client_classes)) {
  90. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_SUBNET6)
  91. .arg((*subnet)->toText()).arg(address.toText());
  92. return (*subnet);
  93. }
  94. }
  95. // Nothing found.
  96. return (Subnet6Ptr());
  97. }
  98. Subnet6Ptr
  99. CfgSubnets6::selectSubnet(const std::string& iface_name,
  100. const ClientClasses& client_classes) const {
  101. // If empty interface specified, we can't select subnet by interface.
  102. if (!iface_name.empty()) {
  103. for (Subnet6Collection::const_iterator subnet = subnets_.begin();
  104. subnet != subnets_.end(); ++subnet) {
  105. // If interface name matches with the one specified for the subnet
  106. // and the client is not rejected based on the classification,
  107. // return the subnet.
  108. if ((iface_name == (*subnet)->getIface()) &&
  109. (*subnet)->clientSupported(client_classes)) {
  110. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
  111. DHCPSRV_CFGMGR_SUBNET6_IFACE)
  112. .arg((*subnet)->toText()).arg(iface_name);
  113. return (*subnet);
  114. }
  115. }
  116. }
  117. // No subnet found for this interface name.
  118. return (Subnet6Ptr());
  119. }
  120. Subnet6Ptr
  121. CfgSubnets6::selectSubnet(const OptionPtr& interface_id,
  122. const ClientClasses& client_classes) const {
  123. // We can only select subnet using an interface id, if the interface
  124. // id is known.
  125. if (interface_id) {
  126. for (Subnet6Collection::const_iterator subnet = subnets_.begin();
  127. subnet != subnets_.end(); ++subnet) {
  128. // If interface id matches for the subnet and the subnet is not
  129. // rejected based on the classification.
  130. if ((*subnet)->getInterfaceId() &&
  131. (*subnet)->getInterfaceId()->equals(interface_id) &&
  132. (*subnet)->clientSupported(client_classes)) {
  133. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
  134. DHCPSRV_CFGMGR_SUBNET6_IFACE_ID)
  135. .arg((*subnet)->toText());
  136. return (*subnet);
  137. }
  138. }
  139. }
  140. // No subnet found.
  141. return (Subnet6Ptr());
  142. }
  143. bool
  144. CfgSubnets6::isDuplicate(const Subnet6& subnet) const {
  145. for (Subnet6Collection::const_iterator subnet_it = subnets_.begin();
  146. subnet_it != subnets_.end(); ++subnet_it) {
  147. if ((*subnet_it)->getID() == subnet.getID()) {
  148. return (true);
  149. }
  150. }
  151. return (false);
  152. }
  153. void
  154. CfgSubnets6::removeStatistics() {
  155. using namespace isc::stats;
  156. StatsMgr& stats_mgr = StatsMgr::instance();
  157. // For each v6 subnet currently configured, remove the statistics.
  158. for (Subnet6Collection::const_iterator subnet6 = subnets_.begin();
  159. subnet6 != subnets_.end(); ++subnet6) {
  160. SubnetID subnet_id = (*subnet6)->getID();
  161. stats_mgr.del(StatsMgr::generateName("subnet", subnet_id, "total-nas"));
  162. stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
  163. "assigned-nas"));
  164. stats_mgr.del(StatsMgr::generateName("subnet", subnet_id, "total-pds"));
  165. stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
  166. "assigned-pds"));
  167. stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
  168. "declined-addresses"));
  169. stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
  170. "declined-reclaimed-addresses"));
  171. stats_mgr.del(StatsMgr::generateName("subnet", subnet_id,
  172. "reclaimed-leases"));
  173. }
  174. }
  175. void
  176. CfgSubnets6::updateStatistics() {
  177. using namespace isc::stats;
  178. StatsMgr& stats_mgr = StatsMgr::instance();
  179. // For each v6 subnet currently configured, calculate totals
  180. for (Subnet6Collection::const_iterator subnet6 = subnets_.begin();
  181. subnet6 != subnets_.end(); ++subnet6) {
  182. SubnetID subnet_id = (*subnet6)->getID();
  183. stats_mgr.setValue(StatsMgr::generateName("subnet", subnet_id,
  184. "total-nas"),
  185. static_cast<int64_t>
  186. ((*subnet6)->getPoolCapacity(Lease::TYPE_NA)));
  187. stats_mgr.setValue(StatsMgr::generateName("subnet", subnet_id,
  188. "total-pds"),
  189. static_cast<int64_t>
  190. ((*subnet6)->getPoolCapacity(Lease::TYPE_PD)));
  191. }
  192. // Only recount the stats if we have subnets.
  193. if (subnets_.begin() != subnets_.end()) {
  194. LeaseMgrFactory::instance().recountLeaseStats6();
  195. }
  196. }
  197. ElementPtr
  198. CfgSubnets6::toElement() const {
  199. ElementPtr result = Element::createList();
  200. // Iterate subnets
  201. for (Subnet6Collection::const_iterator subnet = subnets_.cbegin();
  202. subnet != subnets_.cend(); ++subnet) {
  203. // Prepare the map
  204. ElementPtr map = Element::createMap();
  205. // Set subnet id
  206. SubnetID id = (*subnet)->getID();
  207. map->set("id", Element::create(static_cast<long long>(id)));
  208. // Set relay info
  209. const Subnet::RelayInfo& relay_info = (*subnet)->getRelayInfo();
  210. ElementPtr relay = Element::createMap();
  211. relay->set("ip-address", Element::create(relay_info.addr_.toText()));
  212. map->set("relay", relay);
  213. // Set subnet
  214. map->set("subnet", Element::create((*subnet)->toText()));
  215. // Set interface
  216. const std::string& iface = (*subnet)->getIface();
  217. map->set("interface", Element::create(iface));
  218. // Set interface-id
  219. const OptionPtr& ifaceid = (*subnet)->getInterfaceId();
  220. if (ifaceid) {
  221. std::vector<uint8_t> bin = ifaceid->getData();
  222. std::string ifid;
  223. ifid.resize(bin.size());
  224. if (!bin.empty()) {
  225. std::memcpy(&ifid[0], &bin[0], bin.size());
  226. }
  227. map->set("interface-id", Element::create(ifid));
  228. } else {
  229. map->set("interface-id", Element::create(std::string()));
  230. }
  231. // Set renew-timer
  232. map->set("renew-timer",
  233. Element::create(static_cast<long long>
  234. ((*subnet)->getT1().get())));
  235. // Set rebind-timer
  236. map->set("rebind-timer",
  237. Element::create(static_cast<long long>
  238. ((*subnet)->getT2().get())));
  239. // Set preferred-lifetime
  240. map->set("preferred-lifetime",
  241. Element::create(static_cast<long long>
  242. ((*subnet)->getPreferred().get())));
  243. // Set valid-lifetime
  244. map->set("valid-lifetime",
  245. Element::create(static_cast<long long>
  246. ((*subnet)->getValid().get())));
  247. // Set rapid-commit
  248. bool rapid_commit = (*subnet)->getRapidCommit();
  249. map->set("rapid-commit", Element::create(rapid_commit));
  250. // Set pools
  251. const PoolCollection& pools = (*subnet)->getPools(Lease::TYPE_NA);
  252. ElementPtr pool_list = Element::createList();
  253. for (PoolCollection::const_iterator pool = pools.cbegin();
  254. pool != pools.cend(); ++pool) {
  255. // Prepare the map for a pool (@todo move this code to pool.cc)
  256. ElementPtr pool_map = Element::createMap();
  257. // Set pool
  258. const IOAddress& first = (*pool)->getFirstAddress();
  259. const IOAddress& last = (*pool)->getLastAddress();
  260. std::string range = first.toText() + "-" + last.toText();
  261. // Try to output a prefix (vs a range)
  262. int prefix_len = prefixLengthFromRange(first, last);
  263. if (prefix_len >= 0) {
  264. std::ostringstream oss;
  265. oss << first.toText() << "/" << prefix_len;
  266. range = oss.str();
  267. }
  268. pool_map->set("pool", Element::create(range));
  269. // Set user-context
  270. ConstElementPtr context = (*pool)->getContext();
  271. if (!isNull(context)) {
  272. pool_map->set("user-context", context);
  273. }
  274. // Set pool options
  275. ConstCfgOptionPtr opts = (*pool)->getCfgOption();
  276. pool_map->set("option-data", opts->toElement());
  277. // Push on the pool list
  278. pool_list->add(pool_map);
  279. }
  280. map->set("pools", pool_list);
  281. // Set pd-pools
  282. const PoolCollection& pdpools = (*subnet)->getPools(Lease::TYPE_PD);
  283. ElementPtr pdpool_list = Element::createList();
  284. for (PoolCollection::const_iterator pool = pdpools.cbegin();
  285. pool != pdpools.cend(); ++pool) {
  286. // Get it as a Pool6 (@todo move this code to pool.cc)
  287. const Pool6* pdpool = dynamic_cast<Pool6*>(pool->get());
  288. if (!pdpool) {
  289. isc_throw(ToElementError, "invalid pd-pool pointer");
  290. }
  291. // Prepare the map for a pd-pool
  292. ElementPtr pool_map = Element::createMap();
  293. // Set prefix
  294. const IOAddress& prefix = pdpool->getFirstAddress();
  295. pool_map->set("prefix", Element::create(prefix.toText()));
  296. // Set prefix-len (get it from min - max)
  297. const IOAddress& last = pdpool->getLastAddress();
  298. int prefix_len = prefixLengthFromRange(prefix, last);
  299. if (prefix_len < 0) {
  300. // The pool is bad: give up
  301. isc_throw(ToElementError, "invalid prefix range "
  302. << prefix.toText() << "-" << last.toText());
  303. }
  304. pool_map->set("prefix-len", Element::create(prefix_len));
  305. // Set delegated-len
  306. uint8_t len = pdpool->getLength();
  307. pool_map->set("delegated-len",
  308. Element::create(static_cast<int>(len)));
  309. // Set excluded prefix
  310. const Option6PDExcludePtr& xopt =
  311. pdpool->getPrefixExcludeOption();
  312. if (xopt) {
  313. const IOAddress& xprefix =
  314. xopt->getExcludedPrefix(prefix, len);
  315. pool_map->set("excluded-prefix",
  316. Element::create(xprefix.toText()));
  317. uint8_t xlen = xopt->getExcludedPrefixLength();
  318. pool_map->set("excluded-prefix-len",
  319. Element::create(static_cast<int>(xlen)));
  320. } else {
  321. pool_map->set("excluded-prefix",
  322. Element::create(std::string("::")));
  323. pool_map->set("excluded-prefix-len", Element::create(0));
  324. }
  325. // Set user-context
  326. ConstElementPtr context = pdpool->getContext();
  327. if (!isNull(context)) {
  328. pool_map->set("user-context", context);
  329. }
  330. // Set pool options
  331. ConstCfgOptionPtr opts = pdpool->getCfgOption();
  332. pool_map->set("option-data", opts->toElement());
  333. // Push on the pool list
  334. pdpool_list->add(pool_map);
  335. }
  336. map->set("pd-pools", pdpool_list);
  337. // Set host reservation-mode
  338. Subnet::HRMode hrmode = (*subnet)->getHostReservationMode();
  339. std::string mode;
  340. switch (hrmode) {
  341. case Subnet::HR_DISABLED:
  342. mode = "disabled";
  343. break;
  344. case Subnet::HR_OUT_OF_POOL:
  345. mode = "out-of-pool";
  346. break;
  347. case Subnet::HR_ALL:
  348. mode = "all";
  349. break;
  350. default:
  351. isc_throw(ToElementError,
  352. "invalid host reservation mode: " << hrmode);
  353. }
  354. map->set("reservation-mode", Element::create(mode));
  355. // Set client-class
  356. const ClientClasses& cclasses = (*subnet)->getClientClasses();
  357. if (cclasses.size() > 1) {
  358. isc_throw(ToElementError, "client-class has too many items: "
  359. << cclasses.size());
  360. } else if (!cclasses.empty()) {
  361. map->set("client-class", Element::create(*cclasses.cbegin()));
  362. }
  363. // Set options
  364. ConstCfgOptionPtr opts = (*subnet)->getCfgOption();
  365. map->set("option-data", opts->toElement());
  366. // Push on the list
  367. result->add(map);
  368. }
  369. return (result);
  370. }
  371. } // end of namespace isc::dhcp
  372. } // end of namespace isc