alloc_engine_utils.cc 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567
  1. // Copyright (C) 2012-2016 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this
  5. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6. #include <config.h>
  7. #include <dhcp/duid.h>
  8. #include <dhcp/dhcp4.h>
  9. #include <dhcp/dhcp6.h>
  10. #include <dhcp/pkt4.h>
  11. #include <dhcp/pkt6.h>
  12. #include <dhcpsrv/host_mgr.h>
  13. #include <dhcpsrv/lease_mgr.h>
  14. #include <dhcpsrv/memfile_lease_mgr.h>
  15. #include <stats/stats_mgr.h>
  16. #include <dhcpsrv/tests/test_utils.h>
  17. #include <dhcpsrv/tests/alloc_engine_utils.h>
  18. #include <hooks/hooks_manager.h>
  19. #include <boost/shared_ptr.hpp>
  20. #include <boost/scoped_ptr.hpp>
  21. #include <iostream>
  22. #include <sstream>
  23. #include <algorithm>
  24. #include <set>
  25. #include <time.h>
  26. using namespace std;
  27. using namespace isc::hooks;
  28. using namespace isc::asiolink;
  29. using namespace isc::stats;
  30. namespace isc {
  31. namespace dhcp {
  32. namespace test {
  33. bool testStatistics(const std::string& stat_name, const int64_t exp_value) {
  34. try {
  35. ObservationPtr observation = StatsMgr::instance().getObservation(stat_name);
  36. if (observation) {
  37. if (observation->getInteger().first != exp_value) {
  38. ADD_FAILURE()
  39. << "value of the observed statistics '"
  40. << stat_name << "' " << "("
  41. << observation->getInteger().first << ") "
  42. << "doesn't match expected value (" << exp_value << ")";
  43. }
  44. return (observation->getInteger().first == exp_value);
  45. } else {
  46. ADD_FAILURE() << "Expected statistic " << stat_name
  47. << " not found.";
  48. }
  49. } catch (...) {
  50. ;
  51. }
  52. return (false);
  53. }
  54. void
  55. AllocEngine4Test::testReuseLease4(const AllocEnginePtr& engine,
  56. Lease4Ptr& existing_lease,
  57. const std::string& addr,
  58. const bool fake_allocation,
  59. ExpectedResult exp_result,
  60. Lease4Ptr& result) {
  61. ASSERT_TRUE(engine);
  62. if (existing_lease) {
  63. // If an existing lease was specified, we need to add it to the
  64. // database. Let's wipe any leases for that address (if any). We
  65. // ignore any errors (previous lease may not exist)
  66. LeaseMgrFactory::instance().deleteLease(existing_lease->addr_);
  67. // Let's add it.
  68. ASSERT_TRUE(LeaseMgrFactory::instance().addLease(existing_lease));
  69. }
  70. // A client comes along, asking specifically for a given address
  71. AllocEngine::ClientContext4 ctx(subnet_, clientid_, hwaddr_,
  72. IOAddress(addr), false, false,
  73. "", fake_allocation);
  74. if (fake_allocation) {
  75. ctx.query_.reset(new Pkt4(DHCPDISCOVER, 1234));
  76. } else {
  77. ctx.query_.reset(new Pkt4(DHCPREQUEST, 1234));
  78. }
  79. result = engine->allocateLease4(ctx);
  80. switch (exp_result) {
  81. case SHOULD_PASS:
  82. ASSERT_TRUE(result);
  83. checkLease4(result);
  84. break;
  85. case SHOULD_FAIL:
  86. ASSERT_FALSE(result);
  87. break;
  88. }
  89. }
  90. Lease4Ptr
  91. AllocEngine4Test::generateDeclinedLease(const std::string& addr,
  92. time_t probation_period,
  93. int32_t expired) {
  94. // There's an assumption that hardware address is always present for IPv4
  95. // packet (always non-null). Client-id is optional (may be null).
  96. HWAddrPtr hwaddr(new HWAddr());
  97. time_t now = time(NULL);
  98. Lease4Ptr declined(new Lease4(addr, hwaddr, ClientIdPtr(), 495,
  99. 100, 200, now, subnet_->getID()));
  100. declined->decline(probation_period);
  101. declined->cltt_ = now - probation_period + expired;
  102. return (declined);
  103. }
  104. AllocEngine6Test::AllocEngine6Test() {
  105. CfgMgr::instance().clear();
  106. factory_.create("type=memfile universe=6 persist=false");
  107. duid_ = DuidPtr(new DUID(std::vector<uint8_t>(8, 0x42)));
  108. iaid_ = 42;
  109. // Create fresh instance of the HostMgr, and drop any previous HostMgr state.
  110. HostMgr::instance().create();
  111. // Let's use odd hardware type to check if there is no Ethernet
  112. // hardcoded anywhere.
  113. const uint8_t mac[] = { 0, 1, 22, 33, 44, 55};
  114. hwaddr_ = HWAddrPtr(new HWAddr(mac, sizeof(mac), HTYPE_FDDI));
  115. // Initialize a subnet and short address pool.
  116. initSubnet(IOAddress("2001:db8:1::"),
  117. IOAddress("2001:db8:1::10"),
  118. IOAddress("2001:db8:1::20"));
  119. initFqdn("", false, false);
  120. }
  121. void
  122. AllocEngine6Test::initSubnet(const asiolink::IOAddress& subnet,
  123. const asiolink::IOAddress& pool_start,
  124. const asiolink::IOAddress& pool_end) {
  125. CfgMgr& cfg_mgr = CfgMgr::instance();
  126. subnet_ = Subnet6Ptr(new Subnet6(subnet, 56, 100, 200, 300, 400));
  127. pool_ = Pool6Ptr(new Pool6(Lease::TYPE_NA, pool_start, pool_end));
  128. subnet_->addPool(pool_);
  129. pd_pool_ = Pool6Ptr(new Pool6(Lease::TYPE_PD, subnet, 56, 64));
  130. subnet_->addPool(pd_pool_);
  131. cfg_mgr.getStagingCfg()->getCfgSubnets6()->add(subnet_);
  132. cfg_mgr.commit();
  133. // By default we pretend our subnet has 100 addresses and prefixes allocated.
  134. StatsMgr::instance().setValue(
  135. StatsMgr::generateName("subnet", subnet_->getID(), "assigned-nas"),
  136. static_cast<int64_t>(100));
  137. StatsMgr::instance().setValue(
  138. StatsMgr::generateName("subnet", subnet_->getID(), "assigned-pds"),
  139. static_cast<int64_t>(100));
  140. }
  141. void
  142. AllocEngine6Test::findReservation(AllocEngine& engine,
  143. AllocEngine::ClientContext6& ctx) {
  144. engine.findReservation(ctx);
  145. // Let's check whether there's a hostname specified in the reservation
  146. if (ctx.host_) {
  147. std::string hostname = ctx.host_->getHostname();
  148. // If there is, let's use it
  149. if (!hostname.empty()) {
  150. ctx.hostname_ = hostname;
  151. }
  152. }
  153. }
  154. HostPtr
  155. AllocEngine6Test::createHost6HWAddr(bool add_to_host_mgr, IPv6Resrv::Type type,
  156. HWAddrPtr& hwaddr, const asiolink::IOAddress& addr,
  157. uint8_t prefix_len) {
  158. HostPtr host(new Host(&hwaddr->hwaddr_[0], hwaddr->hwaddr_.size(),
  159. Host::IDENT_HWADDR, SubnetID(0), subnet_->getID(),
  160. asiolink::IOAddress("0.0.0.0")));
  161. IPv6Resrv resv(type, addr, prefix_len);
  162. host->addReservation(resv);
  163. if (add_to_host_mgr) {
  164. CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host);
  165. CfgMgr::instance().commit();
  166. }
  167. return (host);
  168. }
  169. Lease6Collection
  170. AllocEngine6Test::allocateTest(AllocEngine& engine, const Pool6Ptr& pool,
  171. const asiolink::IOAddress& hint, bool fake,
  172. bool in_pool) {
  173. Lease::Type type = pool->getType();
  174. uint8_t expected_len = pool->getLength();
  175. Pkt6Ptr query(new Pkt6(fake ? DHCPV6_SOLICIT : DHCPV6_REQUEST, 1234));
  176. AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "",
  177. fake, query);
  178. ctx.currentIA().iaid_ = iaid_;
  179. ctx.currentIA().type_ = type;
  180. ctx.currentIA().addHint(hint);
  181. Lease6Collection leases;
  182. findReservation(engine, ctx);
  183. EXPECT_NO_THROW(leases = engine.allocateLeases6(ctx));
  184. for (Lease6Collection::iterator it = leases.begin(); it != leases.end(); ++it) {
  185. // Do all checks on the lease
  186. checkLease6(*it, type, expected_len, in_pool, in_pool);
  187. // Check that context has been updated with allocated addresses or
  188. // prefixes.
  189. checkAllocatedResources(*it, ctx);
  190. // Check that the lease is indeed in LeaseMgr
  191. Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(type,
  192. (*it)->addr_);
  193. if (!fake) {
  194. // This is a real (REQUEST) allocation, the lease must be in the DB
  195. EXPECT_TRUE(from_mgr) << "Lease " << from_mgr->addr_.toText()
  196. << " returned by allocateLeases6(), "
  197. << "but was not present in LeaseMgr";
  198. if (!from_mgr) {
  199. return (leases);
  200. }
  201. // Now check that the lease in LeaseMgr has the same parameters
  202. detailCompareLease(*it, from_mgr);
  203. } else {
  204. // This is a fake (SOLICIT) allocation, the lease must not be in DB
  205. EXPECT_FALSE(from_mgr) << "Lease " << from_mgr->addr_.toText()
  206. << " returned by allocateLeases6(), "
  207. << "was present in LeaseMgr (expected to be"
  208. << " not present)";
  209. if (from_mgr) {
  210. return (leases);
  211. }
  212. }
  213. }
  214. return (leases);
  215. }
  216. Lease6Ptr
  217. AllocEngine6Test::simpleAlloc6Test(const Pool6Ptr& pool, const IOAddress& hint,
  218. bool fake, bool in_pool) {
  219. Lease::Type type = pool->getType();
  220. uint8_t expected_len = pool->getLength();
  221. boost::scoped_ptr<AllocEngine> engine;
  222. EXPECT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE,
  223. 100)));
  224. // We can't use ASSERT macros in non-void methods
  225. EXPECT_TRUE(engine);
  226. if (!engine) {
  227. return (Lease6Ptr());
  228. }
  229. Pkt6Ptr query(new Pkt6(fake ? DHCPV6_SOLICIT : DHCPV6_REQUEST, 1234));
  230. AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", fake, query);
  231. ctx.hwaddr_ = hwaddr_;
  232. ctx.addHostIdentifier(Host::IDENT_HWADDR, hwaddr_->hwaddr_);
  233. ctx.currentIA().iaid_ = iaid_;
  234. ctx.currentIA().type_ = type;
  235. ctx.currentIA().addHint(hint);
  236. findReservation(*engine, ctx);
  237. Lease6Ptr lease;
  238. EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx)));
  239. // Check that we got a lease
  240. EXPECT_TRUE(lease);
  241. if (!lease) {
  242. return (Lease6Ptr());
  243. }
  244. // Do all checks on the lease
  245. checkLease6(lease, type, expected_len, in_pool, in_pool);
  246. // Check that the lease is indeed in LeaseMgr
  247. Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(type, lease->addr_);
  248. if (!fake) {
  249. // This is a real (REQUEST) allocation, the lease must be in the DB
  250. EXPECT_TRUE(from_mgr);
  251. if (!from_mgr) {
  252. return (Lease6Ptr());
  253. }
  254. // Now check that the lease in LeaseMgr has the same parameters
  255. detailCompareLease(lease, from_mgr);
  256. } else {
  257. // This is a fake (SOLICIT) allocation, the lease must not be in DB
  258. EXPECT_FALSE(from_mgr);
  259. if (from_mgr) {
  260. return (Lease6Ptr());
  261. }
  262. }
  263. return (lease);
  264. }
  265. Lease6Collection
  266. AllocEngine6Test::renewTest(AllocEngine& engine, const Pool6Ptr& pool,
  267. AllocEngine::HintContainer& hints,
  268. bool in_pool) {
  269. Lease::Type type = pool->getType();
  270. uint8_t expected_len = pool->getLength();
  271. Pkt6Ptr query(new Pkt6(DHCPV6_RENEW, 1234));
  272. AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "",
  273. false, query);
  274. ctx.currentIA().hints_ = hints;
  275. ctx.currentIA().iaid_ = iaid_;
  276. ctx.currentIA().type_ = type;
  277. findReservation(engine, ctx);
  278. Lease6Collection leases = engine.renewLeases6(ctx);
  279. for (Lease6Collection::iterator it = leases.begin(); it != leases.end(); ++it) {
  280. // Do all checks on the lease
  281. checkLease6(*it, type, expected_len, in_pool, in_pool);
  282. // Check that context has been updated with allocated addresses or
  283. // prefixes.
  284. checkAllocatedResources(*it, ctx);
  285. // Check that the lease is indeed in LeaseMgr
  286. Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(type,
  287. (*it)->addr_);
  288. // This is a real (REQUEST) allocation, the lease must be in the DB
  289. EXPECT_TRUE(from_mgr) << "Lease " << from_mgr->addr_.toText()
  290. << " returned by allocateLeases6(), "
  291. << "but was not present in LeaseMgr";
  292. if (!from_mgr) {
  293. return (leases);
  294. }
  295. // Now check that the lease in LeaseMgr has the same parameters
  296. detailCompareLease(*it, from_mgr);
  297. }
  298. return (leases);
  299. }
  300. void
  301. AllocEngine6Test::allocWithUsedHintTest(Lease::Type type, IOAddress used_addr,
  302. IOAddress requested,
  303. uint8_t expected_pd_len) {
  304. boost::scoped_ptr<AllocEngine> engine;
  305. ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
  306. ASSERT_TRUE(engine);
  307. // Let's create a lease and put it in the LeaseMgr
  308. DuidPtr duid2 = boost::shared_ptr<DUID>(new DUID(vector<uint8_t>(8, 0xff)));
  309. time_t now = time(NULL);
  310. Lease6Ptr used(new Lease6(type, used_addr,
  311. duid2, 1, 2, 3, 4, now, subnet_->getID()));
  312. ASSERT_TRUE(LeaseMgrFactory::instance().addLease(used));
  313. // Another client comes in and request an address that is in pool, but
  314. // unfortunately it is used already. The same address must not be allocated
  315. // twice.
  316. Pkt6Ptr query(new Pkt6(DHCPV6_REQUEST, 1234));
  317. AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", false,
  318. query);
  319. ctx.currentIA().iaid_ = iaid_;
  320. ctx.currentIA().type_ = type;
  321. ctx.currentIA().addHint(requested);
  322. Lease6Ptr lease;
  323. EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx)));
  324. // Check that we got a lease
  325. ASSERT_TRUE(lease);
  326. // Allocated address must be different
  327. EXPECT_NE(used_addr, lease->addr_);
  328. // We should NOT get what we asked for, because it is used already
  329. EXPECT_NE(requested, lease->addr_);
  330. // Do all checks on the lease
  331. checkLease6(lease, type, expected_pd_len);
  332. // Check that the lease is indeed in LeaseMgr
  333. Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease->type_,
  334. lease->addr_);
  335. ASSERT_TRUE(from_mgr);
  336. // Now check that the lease in LeaseMgr has the same parameters
  337. detailCompareLease(lease, from_mgr);
  338. }
  339. void
  340. AllocEngine6Test::allocBogusHint6(Lease::Type type, asiolink::IOAddress hint,
  341. uint8_t expected_pd_len) {
  342. boost::scoped_ptr<AllocEngine> engine;
  343. ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
  344. ASSERT_TRUE(engine);
  345. // Client would like to get a 3000::abc lease, which does not belong to any
  346. // supported lease. Allocation engine should ignore it and carry on
  347. // with the normal allocation
  348. Pkt6Ptr query(new Pkt6(DHCPV6_REQUEST, 1234));
  349. AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", false,
  350. query);
  351. ctx.currentIA().iaid_ = iaid_;
  352. ctx.currentIA().type_ = type;
  353. ctx.currentIA().addHint(hint);
  354. Lease6Ptr lease;
  355. EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx)));
  356. // Check that we got a lease
  357. ASSERT_TRUE(lease);
  358. // We should NOT get what we asked for, because it is used already
  359. EXPECT_NE(hint, lease->addr_);
  360. // Do all checks on the lease
  361. checkLease6(lease, type, expected_pd_len);
  362. // Check that the lease is indeed in LeaseMgr
  363. Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease->type_,
  364. lease->addr_);
  365. ASSERT_TRUE(from_mgr);
  366. // Now check that the lease in LeaseMgr has the same parameters
  367. detailCompareLease(lease, from_mgr);
  368. }
  369. void
  370. AllocEngine6Test::testReuseLease6(const AllocEnginePtr& engine,
  371. Lease6Ptr& existing_lease,
  372. const std::string& addr,
  373. const bool fake_allocation,
  374. ExpectedResult exp_result,
  375. Lease6Ptr& result) {
  376. ASSERT_TRUE(engine);
  377. if (existing_lease) {
  378. // If an existing lease was specified, we need to add it to the
  379. // database. Let's wipe any leases for that address (if any). We
  380. // ignore any errors (previous lease may not exist)
  381. LeaseMgrFactory::instance().deleteLease(existing_lease->addr_);
  382. // Let's add it.
  383. ASSERT_TRUE(LeaseMgrFactory::instance().addLease(existing_lease));
  384. }
  385. // A client comes along, asking specifically for a given address
  386. Pkt6Ptr query(new Pkt6(fake_allocation ? DHCPV6_SOLICIT : DHCPV6_REQUEST, 1234));
  387. AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "",
  388. fake_allocation, query);
  389. ctx.currentIA().iaid_ = iaid_;
  390. ctx.currentIA().addHint(IOAddress(addr));
  391. Lease6Collection leases;
  392. leases = engine->allocateLeases6(ctx);
  393. switch (exp_result) {
  394. case SHOULD_PASS:
  395. ASSERT_FALSE(leases.empty());
  396. ASSERT_EQ(1, leases.size());
  397. result = leases[0];
  398. checkLease6(result, Lease::TYPE_NA, 128);
  399. break;
  400. case SHOULD_FAIL:
  401. ASSERT_TRUE(leases.empty());
  402. break;
  403. }
  404. }
  405. Lease6Ptr
  406. AllocEngine6Test::generateDeclinedLease(const std::string& addr,
  407. time_t probation_period,
  408. int32_t expired) {
  409. Lease6Ptr declined(new Lease6(Lease::TYPE_NA, IOAddress(addr),
  410. duid_, iaid_, 100, 100, 100, 100, subnet_->getID()));
  411. time_t now = time(NULL);
  412. declined->decline(probation_period);
  413. declined->cltt_ = now - probation_period + expired;
  414. return (declined);
  415. }
  416. void
  417. AllocEngine4Test::initSubnet(const asiolink::IOAddress& pool_start,
  418. const asiolink::IOAddress& pool_end) {
  419. CfgMgr& cfg_mgr = CfgMgr::instance();
  420. subnet_ = Subnet4Ptr(new Subnet4(IOAddress("192.0.2.0"), 24, 1, 2, 3));
  421. pool_ = Pool4Ptr(new Pool4(pool_start, pool_end));
  422. subnet_->addPool(pool_);
  423. cfg_mgr.getStagingCfg()->getCfgSubnets4()->add(subnet_);
  424. }
  425. AllocEngine4Test::AllocEngine4Test() {
  426. factory_.create("type=memfile universe=4 persist=false");
  427. // Create fresh instance of the HostMgr, and drop any previous HostMgr state.
  428. HostMgr::instance().create();
  429. clientid_ = ClientIdPtr(new ClientId(vector<uint8_t>(8, 0x44)));
  430. clientid2_ = ClientIdPtr(new ClientId(vector<uint8_t>(8, 0x56)));
  431. uint8_t mac[] = { 0, 1, 22, 33, 44, 55};
  432. // Let's use odd hardware type to check if there is no Ethernet
  433. // hardcoded anywhere.
  434. hwaddr_ = HWAddrPtr(new HWAddr(mac, sizeof(mac), HTYPE_FDDI));
  435. // Allocate different MAC address for the tests that require two
  436. // different MAC addresses.
  437. ++mac[sizeof(mac) - 1];
  438. hwaddr2_ = HWAddrPtr(new HWAddr(mac, sizeof (mac), HTYPE_FDDI));
  439. // instantiate cfg_mgr
  440. CfgMgr& cfg_mgr = CfgMgr::instance();
  441. initSubnet(IOAddress("192.0.2.100"), IOAddress("192.0.2.109"));
  442. cfg_mgr.commit();
  443. // Create a default context. Note that remaining parameters must be
  444. // assigned when needed.
  445. ctx_.subnet_ = subnet_;
  446. ctx_.clientid_ = clientid_;
  447. ctx_.hwaddr_ = hwaddr_;
  448. ctx_.callout_handle_ = HooksManager::createCalloutHandle();
  449. ctx_.query_.reset(new Pkt4(DHCPREQUEST, 1234));
  450. }
  451. }; // namespace test
  452. }; // namespace dhcp
  453. }; // namespace isc