dhcp6_test_utils.h 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. // Copyright (C) 2013 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. /// @file dhcp6_test_utils.h
  15. ///
  16. /// @brief This file contains utility classes used for DHCPv6 server testing
  17. #ifndef DHCP6_TEST_UTILS_H
  18. #define DHCP6_TEST_UTILS_H
  19. #include <gtest/gtest.h>
  20. #include <dhcp/pkt6.h>
  21. #include <dhcp/option6_ia.h>
  22. #include <dhcp/option6_iaaddr.h>
  23. #include <dhcp/option6_iaprefix.h>
  24. #include <dhcp/option_int_array.h>
  25. #include <dhcp/option_custom.h>
  26. #include <dhcp/iface_mgr.h>
  27. #include <dhcpsrv/cfgmgr.h>
  28. #include <dhcpsrv/lease_mgr.h>
  29. #include <dhcpsrv/lease_mgr_factory.h>
  30. #include <dhcp6/dhcp6_srv.h>
  31. #include <hooks/hooks_manager.h>
  32. #include <config/ccsession.h>
  33. #include <list>
  34. using namespace isc::dhcp;
  35. using namespace isc::config;
  36. using namespace isc::data;
  37. using namespace isc::hooks;
  38. using namespace isc::asiolink;
  39. using namespace isc::util;
  40. using namespace isc::hooks;
  41. namespace isc {
  42. namespace test {
  43. /// @brief "naked" Dhcpv6Srv class that exposes internal members
  44. class NakedDhcpv6Srv: public isc::dhcp::Dhcpv6Srv {
  45. public:
  46. NakedDhcpv6Srv(uint16_t port) : Dhcpv6Srv(port) {
  47. // Open the "memfile" database for leases
  48. std::string memfile = "type=memfile";
  49. LeaseMgrFactory::create(memfile);
  50. }
  51. /// @brief fakes packet reception
  52. /// @param timeout ignored
  53. ///
  54. /// The method receives all packets queued in receive
  55. /// queue, one after another. Once the queue is empty,
  56. /// it initiates the shutdown procedure.
  57. ///
  58. /// See fake_received_ field for description
  59. virtual isc::dhcp::Pkt6Ptr receivePacket(int /*timeout*/) {
  60. // If there is anything prepared as fake incoming
  61. // traffic, use it
  62. if (!fake_received_.empty()) {
  63. Pkt6Ptr pkt = fake_received_.front();
  64. fake_received_.pop_front();
  65. return (pkt);
  66. }
  67. // If not, just trigger shutdown and
  68. // return immediately
  69. shutdown();
  70. return (Pkt6Ptr());
  71. }
  72. /// @brief fake packet sending
  73. ///
  74. /// Pretend to send a packet, but instead just store
  75. /// it in fake_send_ list where test can later inspect
  76. /// server's response.
  77. virtual void sendPacket(const Pkt6Ptr& pkt) {
  78. fake_sent_.push_back(pkt);
  79. }
  80. /// @brief adds a packet to fake receive queue
  81. ///
  82. /// See fake_received_ field for description
  83. void fakeReceive(const Pkt6Ptr& pkt) {
  84. fake_received_.push_back(pkt);
  85. }
  86. virtual ~NakedDhcpv6Srv() {
  87. // Close the lease database
  88. LeaseMgrFactory::destroy();
  89. }
  90. using Dhcpv6Srv::processSolicit;
  91. using Dhcpv6Srv::processRequest;
  92. using Dhcpv6Srv::processRenew;
  93. using Dhcpv6Srv::processRelease;
  94. using Dhcpv6Srv::processClientFqdn;
  95. using Dhcpv6Srv::createNameChangeRequests;
  96. using Dhcpv6Srv::createRemovalNameChangeRequest;
  97. using Dhcpv6Srv::createStatusCode;
  98. using Dhcpv6Srv::selectSubnet;
  99. using Dhcpv6Srv::sanityCheck;
  100. using Dhcpv6Srv::loadServerID;
  101. using Dhcpv6Srv::writeServerID;
  102. using Dhcpv6Srv::name_change_reqs_;
  103. /// @brief packets we pretend to receive
  104. ///
  105. /// Instead of setting up sockets on interfaces that change between OSes, it
  106. /// is much easier to fake packet reception. This is a list of packets that
  107. /// we pretend to have received. You can schedule new packets to be received
  108. /// using fakeReceive() and NakedDhcpv6Srv::receivePacket() methods.
  109. std::list<Pkt6Ptr> fake_received_;
  110. std::list<Pkt6Ptr> fake_sent_;
  111. };
  112. static const char* DUID_FILE = "server-id-test.txt";
  113. // test fixture for any tests requiring blank/empty configuration
  114. // serves as base class for additional tests
  115. class NakedDhcpv6SrvTest : public ::testing::Test {
  116. public:
  117. NakedDhcpv6SrvTest() : rcode_(-1) {
  118. // it's ok if that fails. There should not be such a file anyway
  119. unlink(DUID_FILE);
  120. const IfaceMgr::IfaceCollection& ifaces = IfaceMgr::instance().getIfaces();
  121. // There must be some interface detected
  122. if (ifaces.empty()) {
  123. // We can't use ASSERT in constructor
  124. ADD_FAILURE() << "No interfaces detected.";
  125. }
  126. valid_iface_ = ifaces.begin()->getName();
  127. }
  128. // Generate IA_NA or IA_PD option with specified parameters
  129. boost::shared_ptr<Option6IA> generateIA(uint16_t type, uint32_t iaid,
  130. uint32_t t1, uint32_t t2);
  131. /// @brief generates interface-id option, based on text
  132. ///
  133. /// @param iface_id textual representation of the interface-id content
  134. ///
  135. /// @return pointer to the option object
  136. OptionPtr generateInterfaceId(const std::string& iface_id) {
  137. OptionBuffer tmp(iface_id.begin(), iface_id.end());
  138. return OptionPtr(new Option(Option::V6, D6O_INTERFACE_ID, tmp));
  139. }
  140. // Generate client-id option
  141. OptionPtr generateClientId(size_t duid_size = 32) {
  142. OptionBuffer clnt_duid(duid_size);
  143. for (int i = 0; i < duid_size; i++) {
  144. clnt_duid[i] = 100 + i;
  145. }
  146. duid_ = DuidPtr(new DUID(clnt_duid));
  147. return (OptionPtr(new Option(Option::V6, D6O_CLIENTID,
  148. clnt_duid.begin(),
  149. clnt_duid.begin() + duid_size)));
  150. }
  151. // Checks if server response (ADVERTISE or REPLY) includes proper server-id.
  152. void checkServerId(const Pkt6Ptr& rsp, const OptionPtr& expected_srvid) {
  153. // check that server included its server-id
  154. OptionPtr tmp = rsp->getOption(D6O_SERVERID);
  155. EXPECT_EQ(tmp->getType(), expected_srvid->getType() );
  156. ASSERT_EQ(tmp->len(), expected_srvid->len() );
  157. EXPECT_TRUE(tmp->getData() == expected_srvid->getData());
  158. }
  159. // Checks if server response (ADVERTISE or REPLY) includes proper client-id.
  160. void checkClientId(const Pkt6Ptr& rsp, const OptionPtr& expected_clientid) {
  161. // check that server included our own client-id
  162. OptionPtr tmp = rsp->getOption(D6O_CLIENTID);
  163. ASSERT_TRUE(tmp);
  164. EXPECT_EQ(expected_clientid->getType(), tmp->getType());
  165. ASSERT_EQ(expected_clientid->len(), tmp->len());
  166. // check that returned client-id is valid
  167. EXPECT_TRUE(expected_clientid->getData() == tmp->getData());
  168. }
  169. // Checks if server response is a NAK
  170. void checkNakResponse(const Pkt6Ptr& rsp, uint8_t expected_message_type,
  171. uint32_t expected_transid,
  172. uint16_t expected_status_code) {
  173. // Check if we get response at all
  174. checkResponse(rsp, expected_message_type, expected_transid);
  175. // Check that IA_NA was returned
  176. OptionPtr option_ia_na = rsp->getOption(D6O_IA_NA);
  177. ASSERT_TRUE(option_ia_na);
  178. // check that the status is no address available
  179. boost::shared_ptr<Option6IA> ia = boost::dynamic_pointer_cast<Option6IA>(option_ia_na);
  180. ASSERT_TRUE(ia);
  181. checkIA_NAStatusCode(ia, expected_status_code);
  182. }
  183. // Checks that server rejected IA_NA, i.e. that it has no addresses and
  184. // that expected status code really appears there. In some limited cases
  185. // (reply to RELEASE) it may be used to verify positive case, where
  186. // IA_NA response is expected to not include address.
  187. //
  188. // Status code indicates type of error encountered (in theory it can also
  189. // indicate success, but servers typically don't send success status
  190. // as this is the default result and it saves bandwidth)
  191. void checkIA_NAStatusCode(const boost::shared_ptr<Option6IA>& ia,
  192. uint16_t expected_status_code) {
  193. // Make sure there is no address assigned.
  194. EXPECT_FALSE(ia->getOption(D6O_IAADDR));
  195. // T1, T2 should be zeroed
  196. EXPECT_EQ(0, ia->getT1());
  197. EXPECT_EQ(0, ia->getT2());
  198. OptionCustomPtr status =
  199. boost::dynamic_pointer_cast<OptionCustom>(ia->getOption(D6O_STATUS_CODE));
  200. // It is ok to not include status success as this is the default behavior
  201. if (expected_status_code == STATUS_Success && !status) {
  202. return;
  203. }
  204. EXPECT_TRUE(status);
  205. if (status) {
  206. // We don't have dedicated class for status code, so let's just interpret
  207. // first 2 bytes as status. Remainder of the status code option content is
  208. // just a text explanation what went wrong.
  209. EXPECT_EQ(static_cast<uint16_t>(expected_status_code),
  210. status->readInteger<uint16_t>(0));
  211. }
  212. }
  213. void checkMsgStatusCode(const Pkt6Ptr& msg, uint16_t expected_status) {
  214. OptionCustomPtr status =
  215. boost::dynamic_pointer_cast<OptionCustom>(msg->getOption(D6O_STATUS_CODE));
  216. // It is ok to not include status success as this is the default behavior
  217. if (expected_status == STATUS_Success && !status) {
  218. return;
  219. }
  220. EXPECT_TRUE(status);
  221. if (status) {
  222. // We don't have dedicated class for status code, so let's just interpret
  223. // first 2 bytes as status. Remainder of the status code option content is
  224. // just a text explanation what went wrong.
  225. EXPECT_EQ(static_cast<uint16_t>(expected_status),
  226. status->readInteger<uint16_t>(0));
  227. }
  228. }
  229. // Basic checks for generated response (message type and transaction-id).
  230. void checkResponse(const Pkt6Ptr& rsp, uint8_t expected_message_type,
  231. uint32_t expected_transid) {
  232. ASSERT_TRUE(rsp);
  233. EXPECT_EQ(expected_message_type, rsp->getType());
  234. EXPECT_EQ(expected_transid, rsp->getTransid());
  235. }
  236. virtual ~NakedDhcpv6SrvTest() {
  237. // Let's clean up if there is such a file.
  238. unlink(DUID_FILE);
  239. HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts(
  240. "buffer6_receive");
  241. HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts(
  242. "buffer6_send");
  243. HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts(
  244. "lease6_renew");
  245. HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts(
  246. "lease6_release");
  247. HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts(
  248. "pkt6_receive");
  249. HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts(
  250. "pkt6_send");
  251. HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts(
  252. "subnet6_select");
  253. };
  254. // A DUID used in most tests (typically as client-id)
  255. DuidPtr duid_;
  256. int rcode_;
  257. ConstElementPtr comment_;
  258. // Name of a valid network interface
  259. std::string valid_iface_;
  260. };
  261. // Provides suport for tests against a preconfigured subnet6
  262. // extends upon NakedDhcp6SrvTest
  263. class Dhcpv6SrvTest : public NakedDhcpv6SrvTest {
  264. public:
  265. /// Name of the server-id file (used in server-id tests)
  266. // these are empty for now, but let's keep them around
  267. Dhcpv6SrvTest() {
  268. subnet_ = Subnet6Ptr(new Subnet6(IOAddress("2001:db8:1::"), 48, 1000,
  269. 2000, 3000, 4000));
  270. pool_ = Pool6Ptr(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1:1::"), 64));
  271. subnet_->addPool(pool_);
  272. CfgMgr::instance().deleteSubnets6();
  273. CfgMgr::instance().addSubnet6(subnet_);
  274. // configure PD pool
  275. pd_pool_ = Pool6Ptr(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:1:2::"), 64, 80));
  276. subnet_->addPool(pd_pool_);
  277. }
  278. /// @brief Checks that server response (ADVERTISE or REPLY) contains proper
  279. /// IA_NA option
  280. ///
  281. /// @param rsp server's response
  282. /// @param expected_iaid expected IAID value
  283. /// @param expected_t1 expected T1 value
  284. /// @param expected_t2 expected T2 value
  285. /// @return IAADDR option for easy chaining with checkIAAddr method
  286. boost::shared_ptr<Option6IAAddr>
  287. checkIA_NA(const Pkt6Ptr& rsp, uint32_t expected_iaid,
  288. uint32_t expected_t1, uint32_t expected_t2);
  289. /// @brief Checks that server response (ADVERTISE or REPLY) contains proper
  290. /// IA_PD option
  291. ///
  292. /// @param rsp server's response
  293. /// @param expected_iaid expected IAID value
  294. /// @param expected_t1 expected T1 value
  295. /// @param expected_t2 expected T2 value
  296. /// @return IAPREFIX option for easy chaining with checkIAAddr method
  297. boost::shared_ptr<Option6IAPrefix>
  298. checkIA_PD(const Pkt6Ptr& rsp, uint32_t expected_iaid,
  299. uint32_t expected_t1, uint32_t expected_t2);
  300. // Check that generated IAADDR option contains expected address
  301. // and lifetime values match the configured subnet
  302. void checkIAAddr(const boost::shared_ptr<Option6IAAddr>& addr,
  303. const IOAddress& expected_addr,
  304. Lease::Type type) {
  305. // Check that the assigned address is indeed from the configured pool.
  306. // Note that when comparing addresses, we compare the textual
  307. // representation. IOAddress does not support being streamed to
  308. // an ostream, which means it can't be used in EXPECT_EQ.
  309. EXPECT_TRUE(subnet_->inPool(type, addr->getAddress()));
  310. EXPECT_EQ(expected_addr.toText(), addr->getAddress().toText());
  311. EXPECT_EQ(addr->getPreferred(), subnet_->getPreferred());
  312. EXPECT_EQ(addr->getValid(), subnet_->getValid());
  313. }
  314. // Checks if the lease sent to client is present in the database
  315. // and is valid when checked agasint the configured subnet
  316. Lease6Ptr checkLease(const DuidPtr& duid, const OptionPtr& ia_na,
  317. boost::shared_ptr<Option6IAAddr> addr);
  318. /// @brief Verifies received IAPrefix option
  319. ///
  320. /// Verifies if received IAPrefix option matches the lease in database
  321. /// @param duid client's DUID
  322. /// @param ia_pd IA_PD option that contains the IAPRefix option
  323. /// @param prefix pointer to the IAPREFIX option
  324. /// @return corresponding IPv6 lease (if found)
  325. Lease6Ptr checkPdLease(const DuidPtr& duid, const OptionPtr& ia_pd,
  326. boost::shared_ptr<Option6IAPrefix> prefix);
  327. /// @brief Creates a message with specified IA
  328. ///
  329. /// An utility function that creates a message of specified type with
  330. /// specified container (IA_NA or IA_PD) and an address or prefix
  331. /// inside it.
  332. ///
  333. /// @param message_type type of the message (e.g. DHCPV6_SOLICIT)
  334. /// @param lease_type type of a lease (TYPE_NA or TYPE_PD)
  335. /// @param addr address or prefix to use in IADDRESS or IAPREFIX options
  336. /// @param prefix_len length of the prefix (used for prefixes only)
  337. /// @param iaid IA identifier (used in IA_XX option)
  338. /// @return created message
  339. Pkt6Ptr
  340. createMessage(uint8_t message_type, Lease::Type lease_type,
  341. const IOAddress& addr, const uint8_t prefix_len,
  342. uint32_t iaid);
  343. /// @brief Performs basic (positive) RENEW test
  344. ///
  345. /// See renewBasic and pdRenewBasic tests for detailed explanation.
  346. /// In essence the test attempts to perform a successful RENEW scenario.
  347. ///
  348. /// This method does not throw, but uses gtest macros to signify failures.
  349. ///
  350. /// @param type type (TYPE_NA or TYPE_PD)
  351. /// @param existing_addr address to be preinserted into the database
  352. /// @param renew_addr address being sent in RENEW
  353. void
  354. testRenewBasic(Lease::Type type, const std::string& existing_addr,
  355. const std::string& renew_addr);
  356. /// @brief Performs negative RENEW test
  357. ///
  358. /// See renewReject and pdRenewReject tests for detailed explanation.
  359. /// In essence the test attempts to perform couple failed RENEW scenarios.
  360. ///
  361. /// This method does not throw, but uses gtest macros to signify failures.
  362. ///
  363. /// @param type type (TYPE_NA or TYPE_PD)
  364. /// @param addr address being sent in RENEW
  365. void
  366. testRenewReject(Lease::Type type, const IOAddress& addr);
  367. /// @brief Performs basic (positive) RELEASE test
  368. ///
  369. /// See releaseBasic and pdReleaseBasic tests for detailed explanation.
  370. /// In essence the test attempts to perform a successful RELEASE scenario.
  371. ///
  372. /// This method does not throw, but uses gtest macros to signify failures.
  373. ///
  374. /// @param type type (TYPE_NA or TYPE_PD)
  375. /// @param existing address to be preinserted into the database
  376. /// @param release_addr address being sent in RELEASE
  377. void
  378. testReleaseBasic(Lease::Type type, const IOAddress& existing,
  379. const IOAddress& release_addr);
  380. ~Dhcpv6SrvTest() {
  381. CfgMgr::instance().deleteSubnets6();
  382. };
  383. /// @brief Performs negative RELEASE test
  384. ///
  385. /// See releaseReject and pdReleaseReject tests for detailed explanation.
  386. /// In essence the test attempts to perform couple failed RELEASE scenarios.
  387. ///
  388. /// This method does not throw, but uses gtest macros to signify failures.
  389. ///
  390. /// @param type type (TYPE_NA or TYPE_PD)
  391. /// @param addr address being sent in RELEASE
  392. void
  393. testReleaseReject(Lease::Type type, const IOAddress& addr);
  394. /// A subnet used in most tests
  395. Subnet6Ptr subnet_;
  396. /// A normal, non-temporary pool used in most tests
  397. Pool6Ptr pool_;
  398. /// A prefix pool used in most tests
  399. Pool6Ptr pd_pool_;
  400. };
  401. }; // end of isc::test namespace
  402. }; // end of isc namespace
  403. #endif // DHCP6_TEST_UTILS_H