cfgmgr.cc 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460
  1. // Copyright (C) 2012-2014 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #include <asiolink/io_address.h>
  15. #include <dhcp/iface_mgr.h>
  16. #include <dhcp/libdhcp++.h>
  17. #include <dhcpsrv/cfgmgr.h>
  18. #include <dhcpsrv/dhcpsrv_log.h>
  19. #include <string>
  20. using namespace isc::asiolink;
  21. using namespace isc::util;
  22. namespace isc {
  23. namespace dhcp {
  24. const size_t CfgMgr::CONFIG_LIST_SIZE = 10;
  25. CfgMgr&
  26. CfgMgr::instance() {
  27. static CfgMgr cfg_mgr;
  28. return (cfg_mgr);
  29. }
  30. void
  31. CfgMgr::addOptionSpace4(const OptionSpacePtr& space) {
  32. if (!space) {
  33. isc_throw(InvalidOptionSpace,
  34. "provided option space object is NULL.");
  35. }
  36. OptionSpaceCollection::iterator it = spaces4_.find(space->getName());
  37. if (it != spaces4_.end()) {
  38. isc_throw(InvalidOptionSpace, "option space " << space->getName()
  39. << " already added.");
  40. }
  41. spaces4_.insert(make_pair(space->getName(), space));
  42. }
  43. void
  44. CfgMgr::addOptionSpace6(const OptionSpacePtr& space) {
  45. if (!space) {
  46. isc_throw(InvalidOptionSpace,
  47. "provided option space object is NULL.");
  48. }
  49. OptionSpaceCollection::iterator it = spaces6_.find(space->getName());
  50. if (it != spaces6_.end()) {
  51. isc_throw(InvalidOptionSpace, "option space " << space->getName()
  52. << " already added.");
  53. }
  54. spaces6_.insert(make_pair(space->getName(), space));
  55. }
  56. void
  57. CfgMgr::addOptionDef(const OptionDefinitionPtr& def,
  58. const std::string& option_space) {
  59. // @todo we need better validation of the provided option space name here.
  60. // This will be implemented when #2313 is merged.
  61. if (option_space.empty()) {
  62. isc_throw(BadValue, "option space name must not be empty");
  63. } else if (!def) {
  64. // Option definition must point to a valid object.
  65. isc_throw(MalformedOptionDefinition, "option definition must not be NULL");
  66. } else if (getOptionDef(option_space, def->getCode())) {
  67. // Option definition must not be overriden.
  68. isc_throw(DuplicateOptionDefinition, "option definition already added"
  69. << " to option space " << option_space);
  70. // We must not override standard (assigned) option for which there is a
  71. // definition in libdhcp++. The standard options belong to dhcp4 or dhcp6
  72. // option space.
  73. } else if ((option_space == "dhcp4" &&
  74. LibDHCP::isStandardOption(Option::V4, def->getCode()) &&
  75. LibDHCP::getOptionDef(Option::V4, def->getCode())) ||
  76. (option_space == "dhcp6" &&
  77. LibDHCP::isStandardOption(Option::V6, def->getCode()) &&
  78. LibDHCP::getOptionDef(Option::V6, def->getCode()))) {
  79. isc_throw(BadValue, "unable to override definition of option '"
  80. << def->getCode() << "' in standard option space '"
  81. << option_space << "'.");
  82. }
  83. // Actually add a new item.
  84. option_def_spaces_.addItem(def, option_space);
  85. }
  86. OptionDefContainerPtr
  87. CfgMgr::getOptionDefs(const std::string& option_space) const {
  88. // @todo Validate the option space once the #2313 is implemented.
  89. return (option_def_spaces_.getItems(option_space));
  90. }
  91. OptionDefinitionPtr
  92. CfgMgr::getOptionDef(const std::string& option_space,
  93. const uint16_t option_code) const {
  94. // @todo Validate the option space once the #2313 is implemented.
  95. // Get a reference to option definitions for a particular option space.
  96. OptionDefContainerPtr defs = getOptionDefs(option_space);
  97. // If there are no matching option definitions then return the empty pointer.
  98. if (!defs || defs->empty()) {
  99. return (OptionDefinitionPtr());
  100. }
  101. // If there are some option definitions for a particular option space
  102. // use an option code to get the one we want.
  103. const OptionDefContainerTypeIndex& idx = defs->get<1>();
  104. const OptionDefContainerTypeRange& range = idx.equal_range(option_code);
  105. // If there is no definition that matches option code, return empty pointer.
  106. if (std::distance(range.first, range.second) == 0) {
  107. return (OptionDefinitionPtr());
  108. }
  109. // If there is more than one definition matching an option code, return
  110. // the first one. This should not happen because we check for duplicates
  111. // when addOptionDef is called.
  112. return (*range.first);
  113. }
  114. Subnet6Ptr
  115. CfgMgr::getSubnet6(const std::string& iface,
  116. const isc::dhcp::ClientClasses& classes) {
  117. if (!iface.length()) {
  118. return (Subnet6Ptr());
  119. }
  120. // If there is more than one, we need to choose the proper one
  121. for (Subnet6Collection::iterator subnet = subnets6_.begin();
  122. subnet != subnets6_.end(); ++subnet) {
  123. // If client is rejected because of not meeting client class criteria...
  124. if (!(*subnet)->clientSupported(classes)) {
  125. continue;
  126. }
  127. if (iface == (*subnet)->getIface()) {
  128. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
  129. DHCPSRV_CFGMGR_SUBNET6_IFACE)
  130. .arg((*subnet)->toText()).arg(iface);
  131. return (*subnet);
  132. }
  133. }
  134. return (Subnet6Ptr());
  135. }
  136. Subnet6Ptr
  137. CfgMgr::getSubnet6(const isc::asiolink::IOAddress& hint,
  138. const isc::dhcp::ClientClasses& classes,
  139. const bool relay) {
  140. // If there is more than one, we need to choose the proper one
  141. for (Subnet6Collection::iterator subnet = subnets6_.begin();
  142. subnet != subnets6_.end(); ++subnet) {
  143. // If client is rejected because of not meeting client class criteria...
  144. if (!(*subnet)->clientSupported(classes)) {
  145. continue;
  146. }
  147. // If the hint is a relay address, and there is relay info specified
  148. // for this subnet and those two match, then use this subnet.
  149. if (relay && ((*subnet)->getRelayInfo().addr_ == hint) ) {
  150. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
  151. DHCPSRV_CFGMGR_SUBNET6_RELAY)
  152. .arg((*subnet)->toText()).arg(hint.toText());
  153. return (*subnet);
  154. }
  155. if ((*subnet)->inRange(hint)) {
  156. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_SUBNET6)
  157. .arg((*subnet)->toText()).arg(hint.toText());
  158. return (*subnet);
  159. }
  160. }
  161. // sorry, we don't support that subnet
  162. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_NO_SUBNET6)
  163. .arg(hint.toText());
  164. return (Subnet6Ptr());
  165. }
  166. Subnet6Ptr CfgMgr::getSubnet6(OptionPtr iface_id_option,
  167. const isc::dhcp::ClientClasses& classes) {
  168. if (!iface_id_option) {
  169. return (Subnet6Ptr());
  170. }
  171. // Let's iterate over all subnets and for those that have interface-id
  172. // defined, check if the interface-id is equal to what we are looking for
  173. for (Subnet6Collection::iterator subnet = subnets6_.begin();
  174. subnet != subnets6_.end(); ++subnet) {
  175. // If client is rejected because of not meeting client class criteria...
  176. if (!(*subnet)->clientSupported(classes)) {
  177. continue;
  178. }
  179. if ( (*subnet)->getInterfaceId() &&
  180. ((*subnet)->getInterfaceId()->equal(iface_id_option))) {
  181. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
  182. DHCPSRV_CFGMGR_SUBNET6_IFACE_ID)
  183. .arg((*subnet)->toText());
  184. return (*subnet);
  185. }
  186. }
  187. return (Subnet6Ptr());
  188. }
  189. void CfgMgr::addSubnet6(const Subnet6Ptr& subnet) {
  190. /// @todo: Check that this new subnet does not cross boundaries of any
  191. /// other already defined subnet.
  192. /// @todo: Check that there is no subnet with the same interface-id
  193. if (isDuplicate(*subnet)) {
  194. isc_throw(isc::dhcp::DuplicateSubnetID, "ID of the new IPv6 subnet '"
  195. << subnet->getID() << "' is already in use");
  196. }
  197. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_ADD_SUBNET6)
  198. .arg(subnet->toText());
  199. subnets6_.push_back(subnet);
  200. }
  201. Subnet4Ptr
  202. CfgMgr::getSubnet4(const isc::asiolink::IOAddress& hint,
  203. const isc::dhcp::ClientClasses& classes,
  204. bool relay) const {
  205. // Iterate over existing subnets to find a suitable one for the
  206. // given address.
  207. for (Subnet4Collection::const_iterator subnet = subnets4_.begin();
  208. subnet != subnets4_.end(); ++subnet) {
  209. // If client is rejected because of not meeting client class criteria...
  210. if (!(*subnet)->clientSupported(classes)) {
  211. continue;
  212. }
  213. // If the hint is a relay address, and there is relay info specified
  214. // for this subnet and those two match, then use this subnet.
  215. if (relay && ((*subnet)->getRelayInfo().addr_ == hint) ) {
  216. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
  217. DHCPSRV_CFGMGR_SUBNET4_RELAY)
  218. .arg((*subnet)->toText()).arg(hint.toText());
  219. return (*subnet);
  220. }
  221. // Let's check if the client belongs to the given subnet
  222. if ((*subnet)->inRange(hint)) {
  223. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
  224. DHCPSRV_CFGMGR_SUBNET4)
  225. .arg((*subnet)->toText()).arg(hint.toText());
  226. return (*subnet);
  227. }
  228. }
  229. // sorry, we don't support that subnet
  230. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_NO_SUBNET4)
  231. .arg(hint.toText());
  232. return (Subnet4Ptr());
  233. }
  234. Subnet4Ptr
  235. CfgMgr::getSubnet4(const std::string& iface_name,
  236. const isc::dhcp::ClientClasses& classes) const {
  237. Iface* iface = IfaceMgr::instance().getIface(iface_name);
  238. // This should never happen in the real life. Hence we throw an exception.
  239. if (iface == NULL) {
  240. isc_throw(isc::BadValue, "interface " << iface_name <<
  241. " doesn't exist and therefore it is impossible"
  242. " to find a suitable subnet for its IPv4 address");
  243. }
  244. IOAddress addr("0.0.0.0");
  245. // If IPv4 address assigned to the interface exists, find a suitable
  246. // subnet for it, else return NULL pointer to indicate that no subnet
  247. // could be found.
  248. return (iface->getAddress4(addr) ? getSubnet4(addr, classes) : Subnet4Ptr());
  249. }
  250. void CfgMgr::addSubnet4(const Subnet4Ptr& subnet) {
  251. /// @todo: Check that this new subnet does not cross boundaries of any
  252. /// other already defined subnet.
  253. if (isDuplicate(*subnet)) {
  254. isc_throw(isc::dhcp::DuplicateSubnetID, "ID of the new IPv4 subnet '"
  255. << subnet->getID() << "' is already in use");
  256. }
  257. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_ADD_SUBNET4)
  258. .arg(subnet->toText());
  259. subnets4_.push_back(subnet);
  260. }
  261. void CfgMgr::deleteOptionDefs() {
  262. option_def_spaces_.clearItems();
  263. }
  264. void CfgMgr::deleteSubnets4() {
  265. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_DELETE_SUBNET4);
  266. subnets4_.clear();
  267. }
  268. void CfgMgr::deleteSubnets6() {
  269. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_DELETE_SUBNET6);
  270. subnets6_.clear();
  271. }
  272. std::string CfgMgr::getDataDir() {
  273. return (datadir_);
  274. }
  275. bool
  276. CfgMgr::isDuplicate(const Subnet4& subnet) const {
  277. for (Subnet4Collection::const_iterator subnet_it = subnets4_.begin();
  278. subnet_it != subnets4_.end(); ++subnet_it) {
  279. if ((*subnet_it)->getID() == subnet.getID()) {
  280. return (true);
  281. }
  282. }
  283. return (false);
  284. }
  285. bool
  286. CfgMgr::isDuplicate(const Subnet6& subnet) const {
  287. for (Subnet6Collection::const_iterator subnet_it = subnets6_.begin();
  288. subnet_it != subnets6_.end(); ++subnet_it) {
  289. if ((*subnet_it)->getID() == subnet.getID()) {
  290. return (true);
  291. }
  292. }
  293. return (false);
  294. }
  295. void
  296. CfgMgr::setD2ClientConfig(D2ClientConfigPtr& new_config) {
  297. d2_client_mgr_.setD2ClientConfig(new_config);
  298. }
  299. bool
  300. CfgMgr::ddnsEnabled() {
  301. return (d2_client_mgr_.ddnsEnabled());
  302. }
  303. const D2ClientConfigPtr&
  304. CfgMgr::getD2ClientConfig() const {
  305. return (d2_client_mgr_.getD2ClientConfig());
  306. }
  307. D2ClientMgr&
  308. CfgMgr::getD2ClientMgr() {
  309. return (d2_client_mgr_);
  310. }
  311. void
  312. CfgMgr::ensureCurrentAllocated() {
  313. if (!configuration_ || configs_.empty()) {
  314. configuration_.reset(new Configuration());
  315. configs_.push_back(configuration_);
  316. }
  317. }
  318. void
  319. CfgMgr::clear() {
  320. configs_.clear();
  321. ensureCurrentAllocated();
  322. }
  323. void
  324. CfgMgr::commit() {
  325. ensureCurrentAllocated();
  326. if (!configs_.back()->sequenceEquals(*configuration_)) {
  327. configuration_ = configs_.back();
  328. // Keep track of the maximum size of the configs history. Before adding
  329. // new element, we have to remove the oldest one.
  330. if (configs_.size() > CONFIG_LIST_SIZE) {
  331. ConfigurationList::iterator it = configs_.begin();
  332. std::advance(it, configs_.size() - CONFIG_LIST_SIZE);
  333. configs_.erase(configs_.begin(), it);
  334. }
  335. }
  336. }
  337. void
  338. CfgMgr::rollback() {
  339. ensureCurrentAllocated();
  340. if (!configuration_->sequenceEquals(*configs_.back())) {
  341. configs_.pop_back();
  342. }
  343. }
  344. void
  345. CfgMgr::revert(const size_t index) {
  346. ensureCurrentAllocated();
  347. if (index == 0) {
  348. isc_throw(isc::OutOfRange, "invalid commit index 0 when reverting"
  349. " to an old configuration");
  350. } else if (index > configs_.size() - 1) {
  351. isc_throw(isc::OutOfRange, "unable to revert to commit index '"
  352. << index << "', only '" << configs_.size() - 1
  353. << "' previous commits available");
  354. }
  355. // Let's rollback an existing configuration to make sure that the last
  356. // configuration on the list is the current one. Note that all remaining
  357. // operations in this function should be exception free so there shouldn't
  358. // be a problem that the revert operation fails and the staging
  359. // configuration is destroyed by this rollback.
  360. rollback();
  361. // Get the iterator to the current configuration and then advance to the
  362. // desired one.
  363. ConfigurationList::const_reverse_iterator it = configs_.rbegin();
  364. std::advance(it, index);
  365. // Copy the desired configuration to the new staging configuration. The
  366. // staging configuration is re-created here because we rolled back earlier
  367. // in this function.
  368. (*it)->copy(*getStagingCfg());
  369. // Make the staging configuration a current one.
  370. commit();
  371. }
  372. ConstConfigurationPtr
  373. CfgMgr::getCurrentCfg() {
  374. ensureCurrentAllocated();
  375. return (configuration_);
  376. }
  377. ConfigurationPtr
  378. CfgMgr::getStagingCfg() {
  379. ensureCurrentAllocated();
  380. if (configuration_->sequenceEquals(*configs_.back())) {
  381. uint32_t sequence = configuration_->getSequence();
  382. configs_.push_back(ConfigurationPtr(new Configuration(++sequence)));
  383. }
  384. return (configs_.back());
  385. }
  386. CfgMgr::CfgMgr()
  387. : datadir_(DHCP_DATA_DIR), echo_v4_client_id_(true),
  388. d2_client_mgr_(), verbose_mode_(false) {
  389. // DHCP_DATA_DIR must be set set with -DDHCP_DATA_DIR="..." in Makefile.am
  390. // Note: the definition of DHCP_DATA_DIR needs to include quotation marks
  391. // See AM_CPPFLAGS definition in Makefile.am
  392. }
  393. CfgMgr::~CfgMgr() {
  394. }
  395. }; // end of isc::dhcp namespace
  396. }; // end of isc namespace