memfile_lease_mgr.cc 37 KB

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