memfile_lease_mgr.cc 38 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028
  1. // Copyright (C) 2012-2015 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 <config.h>
  15. #include <dhcpsrv/cfgmgr.h>
  16. #include <dhcpsrv/dhcpsrv_log.h>
  17. #include <dhcpsrv/lease_file_loader.h>
  18. #include <dhcpsrv/memfile_lease_mgr.h>
  19. #include <dhcpsrv/timer_mgr.h>
  20. #include <dhcpsrv/database_connection.h>
  21. #include <exceptions/exceptions.h>
  22. #include <util/pid_file.h>
  23. #include <util/process_spawn.h>
  24. #include <util/signal_set.h>
  25. #include <cstdio>
  26. #include <cstring>
  27. #include <errno.h>
  28. #include <iostream>
  29. #include <limits>
  30. #include <sstream>
  31. namespace {
  32. /// @brief Maximum number of errors to read the leases from the lease file.
  33. const uint32_t MAX_LEASE_ERRORS = 100;
  34. /// @brief A name of the environmental variable specifying the kea-lfc
  35. /// program location.
  36. ///
  37. /// This variable can be set by tests to point to the location of the
  38. /// kea-lfc program within a build directory. If this variable is not
  39. /// set, the backend will use the location of the kea-lfc in the
  40. /// Kea installation directory.
  41. const char* KEA_LFC_EXECUTABLE_ENV_NAME = "KEA_LFC_EXECUTABLE";
  42. } // end of anonymous namespace
  43. using namespace isc::util;
  44. namespace isc {
  45. namespace dhcp {
  46. /// @brief Represents a configuration for Lease File Cleanup.
  47. ///
  48. /// This class is solely used by the @c Memfile_LeaseMgr as a configuration
  49. /// information storage for %Lease File Cleanup. Internally, it creates
  50. /// the interval timer and assigns a callback function (pointer to which is
  51. /// passed in the constructor), which will be called at the specified
  52. /// intervals to perform the cleanup. It is also responsible for creating
  53. /// and maintaing the object which is used to spawn the new process which
  54. /// executes the @c kea-lfc program.
  55. ///
  56. /// This functionality is enclosed in a separate class so as the implementation
  57. /// details are not exposed in the @c Memfile_LeaseMgr header file and
  58. /// to maintain a single place with the LFC configuration, instead of multiple
  59. /// members and functions scattered in the @c Memfile_LeaseMgr class.
  60. class LFCSetup {
  61. public:
  62. /// @brief Constructor.
  63. ///
  64. /// Assigns a pointer to the function triggered to perform the cleanup.
  65. /// This pointer should point to the appropriate method of the
  66. /// @c Memfile_LeaseMgr class.
  67. ///
  68. /// @param callback A pointer to the callback function.
  69. LFCSetup(asiolink::IntervalTimer::Callback callback);
  70. /// @brief Destructor.
  71. ///
  72. /// Unregisters LFC timer.
  73. ~LFCSetup();
  74. /// @brief Sets the new configuration for the %Lease File Cleanup.
  75. ///
  76. /// @param lfc_interval An interval in seconds at which the cleanup should
  77. /// be performed.
  78. /// @param lease_file4 A pointer to the DHCPv4 lease file to be cleaned up
  79. /// or NULL. If this is NULL, the @c lease_file6 must be non-null.
  80. /// @param lease_file6 A pointer to the DHCPv6 lease file to be cleaned up
  81. /// or NULL. If this is NULL, the @c lease_file4 must be non-null.
  82. void setup(const uint32_t lfc_interval,
  83. const boost::shared_ptr<CSVLeaseFile4>& lease_file4,
  84. const boost::shared_ptr<CSVLeaseFile6>& lease_file6);
  85. /// @brief Spawns a new process.
  86. void execute();
  87. /// @brief Checks if the lease file cleanup is in progress.
  88. ///
  89. /// @return true if the lease file cleanup is being executed.
  90. bool isRunning() const;
  91. /// @brief Returns exit code of the last completed cleanup.
  92. int getExitStatus() const;
  93. private:
  94. /// @brief A pointer to the @c ProcessSpawn object used to execute
  95. /// the LFC.
  96. boost::scoped_ptr<util::ProcessSpawn> process_;
  97. /// @brief A pointer to the callback function executed by the timer.
  98. asiolink::IntervalTimer::Callback callback_;
  99. /// @brief A PID of the last executed LFC process.
  100. pid_t pid_;
  101. /// @brief Pointer to the timer manager.
  102. ///
  103. /// We have to hold this pointer here to make sure that the timer
  104. /// manager is not destroyed before the lease manager.
  105. TimerMgrPtr timer_mgr_;
  106. };
  107. LFCSetup::LFCSetup(asiolink::IntervalTimer::Callback callback)
  108. : process_(), callback_(callback), pid_(0),
  109. timer_mgr_(TimerMgr::instance()) {
  110. }
  111. LFCSetup::~LFCSetup() {
  112. try {
  113. // If we're here it means that either the process is terminating
  114. // or we're reconfiguring the server. In the latter case the
  115. // thread has been stopped probably, but we need to handle the
  116. // former case so we call stopThread explicitly here.
  117. timer_mgr_->stopThread();
  118. // This may throw exception if the timer hasn't been registered
  119. // but if the LFC Setup instance exists it means that the timer
  120. // must have been registered or such registration have been
  121. // attempted. The registration may fail if the duplicate timer
  122. // exists or if the TimerMgr's worker thread is running but if
  123. // this happens it is a programming error. In any case, we
  124. // don't want exceptions being thrown from the destructor so
  125. // we just log an error here.
  126. timer_mgr_->unregisterTimer("memfile-lfc");
  127. } catch (const std::exception& ex) {
  128. LOG_ERROR(dhcpsrv_logger, DHCPSRV_MEMFILE_LFC_UNREGISTER_TIMER_FAILED)
  129. .arg(ex.what());
  130. }
  131. }
  132. void
  133. LFCSetup::setup(const uint32_t lfc_interval,
  134. const boost::shared_ptr<CSVLeaseFile4>& lease_file4,
  135. const boost::shared_ptr<CSVLeaseFile6>& lease_file6) {
  136. // If LFC is enabled, we have to setup the interval timer and prepare for
  137. // executing the kea-lfc process.
  138. if (lfc_interval > 0) {
  139. std::string executable;
  140. char* c_executable = getenv(KEA_LFC_EXECUTABLE_ENV_NAME);
  141. if (c_executable == NULL) {
  142. executable = KEA_LFC_EXECUTABLE;
  143. } else {
  144. executable = c_executable;
  145. }
  146. // Start preparing the command line for kea-lfc.
  147. // Gather the base file name.
  148. std::string lease_file = lease_file4 ? lease_file4->getFilename() :
  149. lease_file6->getFilename();
  150. // Create the other names by appending suffixes to the base name.
  151. util::ProcessArgs args;
  152. // Universe: v4 or v6.
  153. args.push_back(lease_file4 ? "-4" : "-6");
  154. // Previous file.
  155. args.push_back("-x");
  156. args.push_back(Memfile_LeaseMgr::appendSuffix(lease_file,
  157. Memfile_LeaseMgr::FILE_PREVIOUS));
  158. // Input file.
  159. args.push_back("-i");
  160. args.push_back(Memfile_LeaseMgr::appendSuffix(lease_file,
  161. Memfile_LeaseMgr::FILE_INPUT));
  162. // Output file.
  163. args.push_back("-o");
  164. args.push_back(Memfile_LeaseMgr::appendSuffix(lease_file,
  165. Memfile_LeaseMgr::FILE_OUTPUT));
  166. // Finish file.
  167. args.push_back("-f");
  168. args.push_back(Memfile_LeaseMgr::appendSuffix(lease_file,
  169. Memfile_LeaseMgr::FILE_FINISH));
  170. // PID file.
  171. args.push_back("-p");
  172. args.push_back(Memfile_LeaseMgr::appendSuffix(lease_file,
  173. Memfile_LeaseMgr::FILE_PID));
  174. // The configuration file is currently unused.
  175. args.push_back("-c");
  176. args.push_back("ignored-path");
  177. // Create the process (do not start it yet).
  178. process_.reset(new util::ProcessSpawn(executable, args));
  179. // Set the timer to call callback function periodically.
  180. LOG_INFO(dhcpsrv_logger, DHCPSRV_MEMFILE_LFC_SETUP).arg(lfc_interval);
  181. // Multiple the lfc_interval value by 1000 as this value specifies
  182. // a timeout in seconds, whereas the setup() method expects the
  183. // timeout in milliseconds.
  184. timer_mgr_->registerTimer("memfile-lfc", callback_, lfc_interval * 1000,
  185. asiolink::IntervalTimer::REPEATING);
  186. timer_mgr_->setup("memfile-lfc");
  187. }
  188. }
  189. void
  190. LFCSetup::execute() {
  191. try {
  192. LOG_INFO(dhcpsrv_logger, DHCPSRV_MEMFILE_LFC_EXECUTE)
  193. .arg(process_->getCommandLine());
  194. pid_ = process_->spawn();
  195. } catch (const ProcessSpawnError&) {
  196. LOG_ERROR(dhcpsrv_logger, DHCPSRV_MEMFILE_LFC_SPAWN_FAIL);
  197. }
  198. }
  199. bool
  200. LFCSetup::isRunning() const {
  201. return (process_ && process_->isRunning(pid_));
  202. }
  203. int
  204. LFCSetup::getExitStatus() const {
  205. if (!process_) {
  206. isc_throw(InvalidOperation, "unable to obtain LFC process exit code: "
  207. " the process is NULL");
  208. }
  209. return (process_->getExitStatus(pid_));
  210. }
  211. // Explicit definition of class static constants. Values are given in the
  212. // declaration so they're not needed here.
  213. const int Memfile_LeaseMgr::MAJOR_VERSION;
  214. const int Memfile_LeaseMgr::MINOR_VERSION;
  215. Memfile_LeaseMgr::Memfile_LeaseMgr(const DatabaseConnection::ParameterMap& parameters)
  216. : LeaseMgr(), lfc_setup_(), conn_(parameters)
  217. {
  218. // Check the universe and use v4 file or v6 file.
  219. std::string universe = conn_.getParameter("universe");
  220. if (universe == "4") {
  221. std::string file4 = initLeaseFilePath(V4);
  222. if (!file4.empty()) {
  223. loadLeasesFromFiles<Lease4, CSVLeaseFile4>(file4, lease_file4_,
  224. storage4_);
  225. }
  226. } else {
  227. std::string file6 = initLeaseFilePath(V6);
  228. if (!file6.empty()) {
  229. loadLeasesFromFiles<Lease6, CSVLeaseFile6>(file6, lease_file6_,
  230. storage6_);
  231. }
  232. }
  233. // If lease persistence have been disabled for both v4 and v6,
  234. // issue a warning. It is ok not to write leases to disk when
  235. // doing testing, but it should not be done in normal server
  236. // operation.
  237. if (!persistLeases(V4) && !persistLeases(V6)) {
  238. LOG_WARN(dhcpsrv_logger, DHCPSRV_MEMFILE_NO_STORAGE);
  239. } else {
  240. lfcSetup();
  241. }
  242. }
  243. Memfile_LeaseMgr::~Memfile_LeaseMgr() {
  244. if (lease_file4_) {
  245. lease_file4_->close();
  246. lease_file4_.reset();
  247. }
  248. if (lease_file6_) {
  249. lease_file6_->close();
  250. lease_file6_.reset();
  251. }
  252. }
  253. std::string
  254. Memfile_LeaseMgr::getDBVersion() {
  255. std::stringstream tmp;
  256. tmp << "Memfile backend " << MAJOR_VERSION;
  257. tmp << "." << MINOR_VERSION;
  258. return (tmp.str());
  259. }
  260. bool
  261. Memfile_LeaseMgr::addLease(const Lease4Ptr& lease) {
  262. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  263. DHCPSRV_MEMFILE_ADD_ADDR4).arg(lease->addr_.toText());
  264. if (getLease4(lease->addr_)) {
  265. // there is a lease with specified address already
  266. return (false);
  267. }
  268. // Try to write a lease to disk first. If this fails, the lease will
  269. // not be inserted to the memory and the disk and in-memory data will
  270. // remain consistent.
  271. if (persistLeases(V4)) {
  272. lease_file4_->append(*lease);
  273. }
  274. storage4_.insert(lease);
  275. return (true);
  276. }
  277. bool
  278. Memfile_LeaseMgr::addLease(const Lease6Ptr& lease) {
  279. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  280. DHCPSRV_MEMFILE_ADD_ADDR6).arg(lease->addr_.toText());
  281. if (getLease6(lease->type_, lease->addr_)) {
  282. // there is a lease with specified address already
  283. return (false);
  284. }
  285. // Try to write a lease to disk first. If this fails, the lease will
  286. // not be inserted to the memory and the disk and in-memory data will
  287. // remain consistent.
  288. if (persistLeases(V6)) {
  289. lease_file6_->append(*lease);
  290. }
  291. storage6_.insert(lease);
  292. return (true);
  293. }
  294. Lease4Ptr
  295. Memfile_LeaseMgr::getLease4(const isc::asiolink::IOAddress& addr) const {
  296. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  297. DHCPSRV_MEMFILE_GET_ADDR4).arg(addr.toText());
  298. const Lease4StorageAddressIndex& idx = storage4_.get<AddressIndexTag>();
  299. Lease4StorageAddressIndex::iterator l = idx.find(addr);
  300. if (l == idx.end()) {
  301. return (Lease4Ptr());
  302. } else {
  303. return (Lease4Ptr(new Lease4(**l)));
  304. }
  305. }
  306. Lease4Collection
  307. Memfile_LeaseMgr::getLease4(const HWAddr& hwaddr) const {
  308. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  309. DHCPSRV_MEMFILE_GET_HWADDR).arg(hwaddr.toText());
  310. Lease4Collection collection;
  311. const Lease4StorageAddressIndex& idx = storage4_.get<AddressIndexTag>();
  312. for(Lease4StorageAddressIndex::const_iterator lease = idx.begin();
  313. lease != idx.end(); ++lease) {
  314. // Every Lease4 has a hardware address, so we can compare it
  315. if ( (*(*lease)->hwaddr_) == hwaddr) {
  316. collection.push_back((*lease));
  317. }
  318. }
  319. return (collection);
  320. }
  321. Lease4Ptr
  322. Memfile_LeaseMgr::getLease4(const HWAddr& hwaddr, SubnetID subnet_id) const {
  323. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  324. DHCPSRV_MEMFILE_GET_SUBID_HWADDR).arg(subnet_id)
  325. .arg(hwaddr.toText());
  326. // Get the index by HW Address and Subnet Identifier.
  327. const Lease4StorageHWAddressSubnetIdIndex& idx =
  328. storage4_.get<HWAddressSubnetIdIndexTag>();
  329. // Try to find the lease using HWAddr and subnet id.
  330. Lease4StorageHWAddressSubnetIdIndex::const_iterator lease =
  331. idx.find(boost::make_tuple(hwaddr.hwaddr_, subnet_id));
  332. // Lease was not found. Return empty pointer to the caller.
  333. if (lease == idx.end()) {
  334. return (Lease4Ptr());
  335. }
  336. // Lease was found. Return it to the caller.
  337. return (Lease4Ptr(new Lease4(**lease)));
  338. }
  339. Lease4Collection
  340. Memfile_LeaseMgr::getLease4(const ClientId& client_id) const {
  341. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  342. DHCPSRV_MEMFILE_GET_CLIENTID).arg(client_id.toText());
  343. Lease4Collection collection;
  344. const Lease4StorageAddressIndex& idx = storage4_.get<AddressIndexTag>();
  345. for(Lease4StorageAddressIndex::const_iterator lease = idx.begin();
  346. lease != idx.end(); ++ lease) {
  347. // client-id is not mandatory in DHCPv4. There can be a lease that does
  348. // not have a client-id. Dereferencing null pointer would be a bad thing
  349. if((*lease)->client_id_ && *(*lease)->client_id_ == client_id) {
  350. collection.push_back((*lease));
  351. }
  352. }
  353. return (collection);
  354. }
  355. Lease4Ptr
  356. Memfile_LeaseMgr::getLease4(const ClientId& client_id,
  357. const HWAddr& hwaddr,
  358. SubnetID subnet_id) const {
  359. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  360. DHCPSRV_MEMFILE_GET_CLIENTID_HWADDR_SUBID).arg(client_id.toText())
  361. .arg(hwaddr.toText())
  362. .arg(subnet_id);
  363. // Get the index by client id, HW address and subnet id.
  364. const Lease4StorageClientIdHWAddressSubnetIdIndex& idx =
  365. storage4_.get<ClientIdHWAddressSubnetIdIndexTag>();
  366. // Try to get the lease using client id, hardware address and subnet id.
  367. Lease4StorageClientIdHWAddressSubnetIdIndex::const_iterator lease =
  368. idx.find(boost::make_tuple(client_id.getClientId(), hwaddr.hwaddr_,
  369. subnet_id));
  370. if (lease == idx.end()) {
  371. // Lease was not found. Return empty pointer to the caller.
  372. return (Lease4Ptr());
  373. }
  374. // Lease was found. Return it to the caller.
  375. return (*lease);
  376. }
  377. Lease4Ptr
  378. Memfile_LeaseMgr::getLease4(const ClientId& client_id,
  379. SubnetID subnet_id) const {
  380. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  381. DHCPSRV_MEMFILE_GET_SUBID_CLIENTID).arg(subnet_id)
  382. .arg(client_id.toText());
  383. // Get the index by client and subnet id.
  384. const Lease4StorageClientIdSubnetIdIndex& idx =
  385. storage4_.get<ClientIdSubnetIdIndexTag>();
  386. // Try to get the lease using client id and subnet id.
  387. Lease4StorageClientIdSubnetIdIndex::const_iterator lease =
  388. idx.find(boost::make_tuple(client_id.getClientId(), subnet_id));
  389. // Lease was not found. Return empty pointer to the caller.
  390. if (lease == idx.end()) {
  391. return (Lease4Ptr());
  392. }
  393. // Lease was found. Return it to the caller.
  394. return (Lease4Ptr(new Lease4(**lease)));
  395. }
  396. Lease6Ptr
  397. Memfile_LeaseMgr::getLease6(Lease::Type type,
  398. const isc::asiolink::IOAddress& addr) const {
  399. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  400. DHCPSRV_MEMFILE_GET_ADDR6)
  401. .arg(addr.toText())
  402. .arg(Lease::typeToText(type));
  403. Lease6Storage::iterator l = storage6_.find(addr);
  404. if (l == storage6_.end() || !(*l) || ((*l)->type_ != type)) {
  405. return (Lease6Ptr());
  406. } else {
  407. return (Lease6Ptr(new Lease6(**l)));
  408. }
  409. }
  410. Lease6Collection
  411. Memfile_LeaseMgr::getLeases6(Lease::Type type,
  412. const DUID& duid, uint32_t iaid) const {
  413. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  414. DHCPSRV_MEMFILE_GET_IAID_DUID)
  415. .arg(iaid)
  416. .arg(duid.toText())
  417. .arg(Lease::typeToText(type));
  418. // Get the index by DUID, IAID, lease type.
  419. const Lease6StorageDuidIaidTypeIndex& idx = storage6_.get<DuidIaidTypeIndexTag>();
  420. // Try to get the lease using the DUID, IAID and lease type.
  421. std::pair<Lease6StorageDuidIaidTypeIndex::const_iterator,
  422. Lease6StorageDuidIaidTypeIndex::const_iterator> l =
  423. idx.equal_range(boost::make_tuple(duid.getDuid(), iaid, type));
  424. Lease6Collection collection;
  425. for(Lease6StorageDuidIaidTypeIndex::const_iterator lease =
  426. l.first; lease != l.second; ++lease) {
  427. collection.push_back(Lease6Ptr(new Lease6(**lease)));
  428. }
  429. return (collection);
  430. }
  431. Lease6Collection
  432. Memfile_LeaseMgr::getLeases6(Lease::Type type,
  433. const DUID& duid, uint32_t iaid,
  434. SubnetID subnet_id) const {
  435. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  436. DHCPSRV_MEMFILE_GET_IAID_SUBID_DUID)
  437. .arg(iaid)
  438. .arg(subnet_id)
  439. .arg(duid.toText())
  440. .arg(Lease::typeToText(type));
  441. // Get the index by DUID, IAID, lease type.
  442. const Lease6StorageDuidIaidTypeIndex& idx = storage6_.get<DuidIaidTypeIndexTag>();
  443. // Try to get the lease using the DUID, IAID and lease type.
  444. std::pair<Lease6StorageDuidIaidTypeIndex::const_iterator,
  445. Lease6StorageDuidIaidTypeIndex::const_iterator> l =
  446. idx.equal_range(boost::make_tuple(duid.getDuid(), iaid, type));
  447. Lease6Collection collection;
  448. for(Lease6StorageDuidIaidTypeIndex::const_iterator lease =
  449. l.first; lease != l.second; ++lease) {
  450. // Filter out the leases which subnet id doesn't match.
  451. if((*lease)->subnet_id_ == subnet_id) {
  452. collection.push_back(Lease6Ptr(new Lease6(**lease)));
  453. }
  454. }
  455. return (collection);
  456. }
  457. void
  458. Memfile_LeaseMgr::getExpiredLeases6(Lease6Collection& expired_leases,
  459. const size_t max_leases) const {
  460. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MEMFILE_GET_EXPIRED4)
  461. .arg(max_leases);
  462. // Obtain the index which segragates leases by state and time.
  463. const Lease6StorageExpirationIndex& index = storage6_.get<ExpirationIndexTag>();
  464. // Retrieve leases which are not reclaimed and which haven't expired. The
  465. // 'less-than' operator will be used for both components of the index. So,
  466. // for the 'state' 'false' is less than 'true'. Also the leases with
  467. // expiration time lower than current time will be returned.
  468. Lease6StorageExpirationIndex::const_iterator ub =
  469. index.upper_bound(boost::make_tuple(false, time(NULL)));
  470. // Copy only the number of leases indicated by the max_leases parameter.
  471. for (Lease6StorageExpirationIndex::const_iterator lease = index.begin();
  472. (lease != ub) && ((max_leases == 0) || (std::distance(index.begin(), lease) <
  473. max_leases));
  474. ++lease) {
  475. expired_leases.push_back(Lease6Ptr(new Lease6(**lease)));
  476. }
  477. }
  478. void
  479. Memfile_LeaseMgr::getExpiredLeases4(Lease4Collection& expired_leases,
  480. const size_t max_leases) const {
  481. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MEMFILE_GET_EXPIRED6)
  482. .arg(max_leases);
  483. // Obtain the index which segragates leases by state and time.
  484. const Lease4StorageExpirationIndex& index = storage4_.get<ExpirationIndexTag>();
  485. // Retrieve leases which are not reclaimed and which haven't expired. The
  486. // 'less-than' operator will be used for both components of the index. So,
  487. // for the 'state' 'false' is less than 'true'. Also the leases with
  488. // expiration time lower than current time will be returned.
  489. Lease4StorageExpirationIndex::const_iterator ub =
  490. index.upper_bound(boost::make_tuple(false, time(NULL)));
  491. // Copy only the number of leases indicated by the max_leases parameter.
  492. for (Lease4StorageExpirationIndex::const_iterator lease = index.begin();
  493. (lease != ub) && ((max_leases == 0) || (std::distance(index.begin(), lease) <
  494. max_leases));
  495. ++lease) {
  496. expired_leases.push_back(Lease4Ptr(new Lease4(**lease)));
  497. }
  498. }
  499. void
  500. Memfile_LeaseMgr::updateLease4(const Lease4Ptr& lease) {
  501. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  502. DHCPSRV_MEMFILE_UPDATE_ADDR4).arg(lease->addr_.toText());
  503. // Obtain 'by address' index.
  504. Lease4StorageAddressIndex& index = storage4_.get<AddressIndexTag>();
  505. // Lease must exist if it is to be updated.
  506. Lease4StorageAddressIndex::const_iterator lease_it = index.find(lease->addr_);
  507. if (lease_it == index.end()) {
  508. isc_throw(NoSuchLease, "failed to update the lease with address "
  509. << lease->addr_ << " - no such lease");
  510. }
  511. // Try to write a lease to disk first. If this fails, the lease will
  512. // not be inserted to the memory and the disk and in-memory data will
  513. // remain consistent.
  514. if (persistLeases(V4)) {
  515. lease_file4_->append(*lease);
  516. }
  517. // Use replace() to re-index leases.
  518. index.replace(lease_it, Lease4Ptr(new Lease4(*lease)));
  519. }
  520. void
  521. Memfile_LeaseMgr::updateLease6(const Lease6Ptr& lease) {
  522. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  523. DHCPSRV_MEMFILE_UPDATE_ADDR6).arg(lease->addr_.toText());
  524. // Obtain 'by address' index.
  525. Lease6StorageAddressIndex& index = storage6_.get<AddressIndexTag>();
  526. // Lease must exist if it is to be updated.
  527. Lease6StorageAddressIndex::const_iterator lease_it = index.find(lease->addr_);
  528. if (lease_it == index.end()) {
  529. isc_throw(NoSuchLease, "failed to update the lease with address "
  530. << lease->addr_ << " - no such lease");
  531. }
  532. // Try to write a lease to disk first. If this fails, the lease will
  533. // not be inserted to the memory and the disk and in-memory data will
  534. // remain consistent.
  535. if (persistLeases(V6)) {
  536. lease_file6_->append(*lease);
  537. }
  538. // Use replace() to re-index leases.
  539. index.replace(lease_it, Lease6Ptr(new Lease6(*lease)));
  540. }
  541. bool
  542. Memfile_LeaseMgr::deleteLease(const isc::asiolink::IOAddress& addr) {
  543. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  544. DHCPSRV_MEMFILE_DELETE_ADDR).arg(addr.toText());
  545. if (addr.isV4()) {
  546. // v4 lease
  547. Lease4Storage::iterator l = storage4_.find(addr);
  548. if (l == storage4_.end()) {
  549. // No such lease
  550. return (false);
  551. } else {
  552. if (persistLeases(V4)) {
  553. // Copy the lease. The valid lifetime needs to be modified and
  554. // we don't modify the original lease.
  555. Lease4 lease_copy = **l;
  556. // Setting valid lifetime to 0 means that lease is being
  557. // removed.
  558. lease_copy.valid_lft_ = 0;
  559. lease_file4_->append(lease_copy);
  560. }
  561. storage4_.erase(l);
  562. return (true);
  563. }
  564. } else {
  565. // v6 lease
  566. Lease6Storage::iterator l = storage6_.find(addr);
  567. if (l == storage6_.end()) {
  568. // No such lease
  569. return (false);
  570. } else {
  571. if (persistLeases(V6)) {
  572. // Copy the lease. The lifetimes need to be modified and we
  573. // don't modify the original lease.
  574. Lease6 lease_copy = **l;
  575. // Setting lifetimes to 0 means that lease is being removed.
  576. lease_copy.valid_lft_ = 0;
  577. lease_copy.preferred_lft_ = 0;
  578. lease_file6_->append(lease_copy);
  579. }
  580. storage6_.erase(l);
  581. return (true);
  582. }
  583. }
  584. }
  585. uint64_t
  586. Memfile_LeaseMgr::deleteExpiredReclaimedLeases4(const uint32_t secs) {
  587. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  588. DHCPSRV_MEMFILE_DELETE_EXPIRED_RECLAIMED4)
  589. .arg(secs);
  590. return (deleteExpiredReclaimedLeases<
  591. Lease4StorageExpirationIndex, Lease4
  592. >(secs, V4, storage4_, lease_file4_));
  593. }
  594. uint64_t
  595. Memfile_LeaseMgr::deleteExpiredReclaimedLeases6(const uint32_t secs) {
  596. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  597. DHCPSRV_MEMFILE_DELETE_EXPIRED_RECLAIMED6)
  598. .arg(secs);
  599. return (deleteExpiredReclaimedLeases<
  600. Lease6StorageExpirationIndex, Lease6
  601. >(secs, V6, storage6_, lease_file6_));
  602. }
  603. template<typename IndexType, typename LeaseType, typename StorageType,
  604. typename LeaseFileType>
  605. uint64_t
  606. Memfile_LeaseMgr::deleteExpiredReclaimedLeases(const uint32_t secs,
  607. const Universe& universe,
  608. StorageType& storage,
  609. LeaseFileType& lease_file) const {
  610. // Obtain the index which segragates leases by state and time.
  611. IndexType& index = storage.template get<ExpirationIndexTag>();
  612. // This returns the first element which is greater than the specified
  613. // tuple (true, time(NULL) - secs). However, the range between the
  614. // beginnng of the index and returned element also includes all the
  615. // elements for which the first value is false (lease state is NOT
  616. // reclaimed), because false < true. All elements between the
  617. // beginning of the index and the element returned, for which the
  618. // first value is true, represent the reclaimed leases which should
  619. // be deleted, because their expiration time + secs has occured earlier
  620. // than current time.
  621. typename IndexType::const_iterator upper_limit =
  622. index.upper_bound(boost::make_tuple(true, time(NULL) - secs));
  623. // Now, we have to exclude all elements of the index which represent
  624. // leases in the state other than reclaimed - with the first value
  625. // in the index equal to false. Note that elements in the index are
  626. // ordered from the lower to the higher ones. So, all elements with
  627. // the first value of false are placed before the elements with the
  628. // value of true. Hence, we have to find the first element which
  629. // contains value of true. The time value is the lowest possible.
  630. typename IndexType::const_iterator lower_limit =
  631. index.upper_bound(boost::make_tuple(true, std::numeric_limits<int64_t>::min()));
  632. // If there are some elements in this range, delete them.
  633. uint64_t num_leases = static_cast<uint64_t>(std::distance(lower_limit, upper_limit));
  634. if (num_leases > 0) {
  635. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  636. DHCPSRV_MEMFILE_DELETE_EXPIRED_RECLAIMED_START)
  637. .arg(num_leases);
  638. // If lease persistence is enabled, we also have to mark leases
  639. // as deleted in the lease file. We do this by setting the
  640. // lifetime to 0.
  641. if (persistLeases(universe)) {
  642. for (typename IndexType::const_iterator lease = lower_limit;
  643. lease != upper_limit; ++lease) {
  644. // Copy lease to not affect the lease in the container.
  645. LeaseType lease_copy(**lease);
  646. // Set the valid lifetime to 0 to indicate the removal
  647. // of the lease.
  648. lease_copy.valid_lft_ = 0;
  649. lease_file->append(lease_copy);
  650. }
  651. }
  652. // Erase leases from memory.
  653. index.erase(lower_limit, upper_limit);
  654. }
  655. // Return number of leases deleted.
  656. return (num_leases);
  657. }
  658. std::string
  659. Memfile_LeaseMgr::getDescription() const {
  660. return (std::string("This is a dummy memfile backend implementation.\n"
  661. "It does not offer any useful lease management and its only\n"
  662. "purpose is to test abstract lease manager API."));
  663. }
  664. void
  665. Memfile_LeaseMgr::commit() {
  666. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MEMFILE_COMMIT);
  667. }
  668. void
  669. Memfile_LeaseMgr::rollback() {
  670. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  671. DHCPSRV_MEMFILE_ROLLBACK);
  672. }
  673. std::string
  674. Memfile_LeaseMgr::appendSuffix(const std::string& file_name,
  675. const LFCFileType& file_type) {
  676. std::string name(file_name);
  677. switch (file_type) {
  678. case FILE_INPUT:
  679. name += ".1";
  680. break;
  681. case FILE_PREVIOUS:
  682. name += ".2";
  683. break;
  684. case FILE_OUTPUT:
  685. name += ".output";
  686. break;
  687. case FILE_FINISH:
  688. name += ".completed";
  689. break;
  690. case FILE_PID:
  691. name += ".pid";
  692. break;
  693. default:
  694. // Do not append any suffix for the FILE_CURRENT.
  695. ;
  696. }
  697. return (name);
  698. }
  699. std::string
  700. Memfile_LeaseMgr::getDefaultLeaseFilePath(Universe u) const {
  701. std::ostringstream s;
  702. s << CfgMgr::instance().getDataDir() << "/kea-leases";
  703. s << (u == V4 ? "4" : "6");
  704. s << ".csv";
  705. return (s.str());
  706. }
  707. std::string
  708. Memfile_LeaseMgr::getLeaseFilePath(Universe u) const {
  709. if (u == V4) {
  710. return (lease_file4_ ? lease_file4_->getFilename() : "");
  711. }
  712. return (lease_file6_ ? lease_file6_->getFilename() : "");
  713. }
  714. bool
  715. Memfile_LeaseMgr::persistLeases(Universe u) const {
  716. // Currently, if the lease file IO is not created, it means that writes to
  717. // disk have been explicitly disabled by the administrator. At some point,
  718. // there may be a dedicated ON/OFF flag implemented to control this.
  719. if (u == V4 && lease_file4_) {
  720. return (true);
  721. }
  722. return (u == V6 && lease_file6_);
  723. }
  724. std::string
  725. Memfile_LeaseMgr::initLeaseFilePath(Universe u) {
  726. std::string persist_val;
  727. try {
  728. persist_val = conn_.getParameter("persist");
  729. } catch (const Exception&) {
  730. // If parameter persist hasn't been specified, we use a default value
  731. // 'yes'.
  732. persist_val = "true";
  733. }
  734. // If persist_val is 'false' we will not store leases to disk, so let's
  735. // return empty file name.
  736. if (persist_val == "false") {
  737. return ("");
  738. } else if (persist_val != "true") {
  739. isc_throw(isc::BadValue, "invalid value 'persist="
  740. << persist_val << "'");
  741. }
  742. std::string lease_file;
  743. try {
  744. lease_file = conn_.getParameter("name");
  745. } catch (const Exception&) {
  746. lease_file = getDefaultLeaseFilePath(u);
  747. }
  748. return (lease_file);
  749. }
  750. template<typename LeaseObjectType, typename LeaseFileType, typename StorageType>
  751. void Memfile_LeaseMgr::loadLeasesFromFiles(const std::string& filename,
  752. boost::shared_ptr<LeaseFileType>& lease_file,
  753. StorageType& storage) {
  754. // Check if the instance of the LFC is running right now. If it is
  755. // running, we refuse to load leases as the LFC may be writing to the
  756. // lease files right now. When the user retries server configuration
  757. // it should go through.
  758. /// @todo Consider applying a timeout for an LFC and retry when this
  759. /// timeout elapses.
  760. PIDFile pid_file(appendSuffix(filename, FILE_PID));
  761. if (pid_file.check()) {
  762. isc_throw(DbOpenError, "unable to load leases from files while the "
  763. "lease file cleanup is in progress");
  764. }
  765. storage.clear();
  766. // Load the leasefile.completed, if exists.
  767. lease_file.reset(new LeaseFileType(std::string(filename + ".completed")));
  768. if (lease_file->exists()) {
  769. LeaseFileLoader::load<LeaseObjectType>(*lease_file, storage,
  770. MAX_LEASE_ERRORS);
  771. } else {
  772. // If the leasefile.completed doesn't exist, let's load the leases
  773. // from leasefile.2 and leasefile.1, if they exist.
  774. lease_file.reset(new LeaseFileType(appendSuffix(filename, FILE_PREVIOUS)));
  775. if (lease_file->exists()) {
  776. LeaseFileLoader::load<LeaseObjectType>(*lease_file, storage,
  777. MAX_LEASE_ERRORS);
  778. }
  779. lease_file.reset(new LeaseFileType(appendSuffix(filename, FILE_INPUT)));
  780. if (lease_file->exists()) {
  781. LeaseFileLoader::load<LeaseObjectType>(*lease_file, storage,
  782. MAX_LEASE_ERRORS);
  783. }
  784. }
  785. // Always load leases from the primary lease file. If the lease file
  786. // doesn't exist it will be created by the LeaseFileLoader. Note
  787. // that the false value passed as the last parameter to load
  788. // function causes the function to leave the file open after
  789. // it is parsed. This file will be used by the backend to record
  790. // future lease updates.
  791. lease_file.reset(new LeaseFileType(filename));
  792. LeaseFileLoader::load<LeaseObjectType>(*lease_file, storage,
  793. MAX_LEASE_ERRORS, false);
  794. }
  795. bool
  796. Memfile_LeaseMgr::isLFCRunning() const {
  797. return (lfc_setup_->isRunning());
  798. }
  799. int
  800. Memfile_LeaseMgr::getLFCExitStatus() const {
  801. return (lfc_setup_->getExitStatus());
  802. }
  803. void
  804. Memfile_LeaseMgr::lfcCallback() {
  805. LOG_INFO(dhcpsrv_logger, DHCPSRV_MEMFILE_LFC_START);
  806. // Check if we're in the v4 or v6 space and use the appropriate file.
  807. if (lease_file4_) {
  808. lfcExecute(lease_file4_);
  809. } else if (lease_file6_) {
  810. lfcExecute(lease_file6_);
  811. }
  812. }
  813. void
  814. Memfile_LeaseMgr::lfcSetup() {
  815. std::string lfc_interval_str = "0";
  816. try {
  817. lfc_interval_str = conn_.getParameter("lfc-interval");
  818. } catch (const std::exception&) {
  819. // Ignore and default to 0.
  820. }
  821. uint32_t lfc_interval = 0;
  822. try {
  823. lfc_interval = boost::lexical_cast<uint32_t>(lfc_interval_str);
  824. } catch (boost::bad_lexical_cast&) {
  825. isc_throw(isc::BadValue, "invalid value of the lfc-interval "
  826. << lfc_interval_str << " specified");
  827. }
  828. if (lfc_interval > 0) {
  829. lfc_setup_.reset(new LFCSetup(boost::bind(&Memfile_LeaseMgr::lfcCallback, this)));
  830. lfc_setup_->setup(lfc_interval, lease_file4_, lease_file6_);
  831. }
  832. }
  833. template<typename LeaseFileType>
  834. void Memfile_LeaseMgr::lfcExecute(boost::shared_ptr<LeaseFileType>& lease_file) {
  835. bool do_lfc = true;
  836. // Check the status of the LFC instance.
  837. // If the finish file exists or the copy of the lease file exists it
  838. // is an indication that another LFC instance may be in progress or
  839. // may be stalled. In that case we don't want to rotate the current
  840. // lease file to avoid overriding the contents of the existing file.
  841. CSVFile lease_file_finish(appendSuffix(lease_file->getFilename(), FILE_FINISH));
  842. CSVFile lease_file_copy(appendSuffix(lease_file->getFilename(), FILE_INPUT));
  843. if (!lease_file_finish.exists() && !lease_file_copy.exists()) {
  844. // Close the current file so as we can move it to the copy file.
  845. lease_file->close();
  846. // Move the current file to the copy file. Remember the result
  847. // because we don't want to run LFC if the rename failed.
  848. do_lfc = (rename(lease_file->getFilename().c_str(),
  849. lease_file_copy.getFilename().c_str()) == 0);
  850. if (!do_lfc) {
  851. LOG_ERROR(dhcpsrv_logger, DHCPSRV_MEMFILE_LFC_LEASE_FILE_RENAME_FAIL)
  852. .arg(lease_file->getFilename())
  853. .arg(lease_file_copy.getFilename())
  854. .arg(strerror(errno));
  855. }
  856. // Regardless if we successfully moved the current file or not,
  857. // we need to re-open the current file for the server to write
  858. // new lease updates. If the file has been successfully moved,
  859. // this will result in creation of the new file. Otherwise,
  860. // an existing file will be opened.
  861. try {
  862. lease_file->open(true);
  863. } catch (const CSVFileError& ex) {
  864. // If we're unable to open the lease file this is a serious
  865. // error because the server will not be able to persist
  866. // leases.
  867. /// @todo We need to better address this error. It should
  868. /// trigger an alarm (once we have a monitoring system in
  869. /// place) so as an administrator can correct it. In
  870. /// practice it should be very rare that this happens and
  871. /// is most likely related to a human error, e.g. changing
  872. /// file permissions.
  873. LOG_ERROR(dhcpsrv_logger, DHCPSRV_MEMFILE_LFC_LEASE_FILE_REOPEN_FAIL)
  874. .arg(lease_file->getFilename())
  875. .arg(ex.what());
  876. // Reset the pointer to the file so as the backend doesn't
  877. // try to write leases to disk.
  878. lease_file.reset();
  879. do_lfc = false;
  880. }
  881. }
  882. // Once the files have been rotated, or untouched if another LFC had
  883. // not finished, a new process is started.
  884. if (do_lfc) {
  885. lfc_setup_->execute();
  886. }
  887. }
  888. } // end of namespace isc::dhcp
  889. } // end of namespace isc