memfile_lease_mgr.cc 39 KB

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