fqdn_unittest.cc 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858
  1. // Copyright (C) 2013-2014 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 <asiolink/io_address.h>
  16. #include <dhcp_ddns/ncr_msg.h>
  17. #include <dhcp/dhcp6.h>
  18. #include <dhcp/option.h>
  19. #include <dhcp/option_custom.h>
  20. #include <dhcp/option6_client_fqdn.h>
  21. #include <dhcp/option6_ia.h>
  22. #include <dhcp/option6_iaaddr.h>
  23. #include <dhcp/option_int_array.h>
  24. #include <dhcpsrv/lease.h>
  25. #include <dhcp6/tests/dhcp6_test_utils.h>
  26. #include <boost/pointer_cast.hpp>
  27. #include <gtest/gtest.h>
  28. using namespace isc;
  29. using namespace isc::test;
  30. using namespace isc::asiolink;
  31. using namespace isc::dhcp;
  32. using namespace isc::dhcp_ddns;
  33. using namespace isc::util;
  34. using namespace isc::hooks;
  35. using namespace std;
  36. namespace {
  37. /// @brief A test fixture class for testing DHCPv6 Client FQDN Option handling.
  38. class FqdnDhcpv6SrvTest : public Dhcpv6SrvTest {
  39. public:
  40. /// @brief Constructor
  41. FqdnDhcpv6SrvTest()
  42. : Dhcpv6SrvTest() {
  43. // generateClientId assigns DUID to duid_.
  44. generateClientId();
  45. lease_.reset(new Lease6(Lease::TYPE_NA, IOAddress("2001:db8:1::1"),
  46. duid_, 1234, 501, 502, 503,
  47. 504, 1, 0));
  48. }
  49. /// @brief Destructor
  50. virtual ~FqdnDhcpv6SrvTest() {
  51. }
  52. /// @brief Construct the DHCPv6 Client FQDN option using flags and
  53. /// domain-name.
  54. ///
  55. /// @param flags Flags to be set for the created option.
  56. /// @param fqdn_name A name which should be stored in the option.
  57. /// @param fqdn_type A type of the name carried by the option: partial
  58. /// or fully qualified.
  59. ///
  60. /// @return A pointer to the created option.
  61. Option6ClientFqdnPtr
  62. createClientFqdn(const uint8_t flags,
  63. const std::string& fqdn_name,
  64. const Option6ClientFqdn::DomainNameType fqdn_type) {
  65. return (Option6ClientFqdnPtr(new Option6ClientFqdn(flags,
  66. fqdn_name,
  67. fqdn_type)));
  68. }
  69. /// @brief Create a message with or without DHCPv6 Client FQDN Option.
  70. ///
  71. /// @param msg_type A type of the DHCPv6 message to be created.
  72. /// @param fqdn_flags Flags to be carried in the FQDN option.
  73. /// @param fqdn_domain_name A name to be carried in the FQDN option.
  74. /// @param fqdn_type A type of the name carried by the option: partial
  75. /// or fully qualified.
  76. /// @param include_oro A boolean value which indicates whether the ORO
  77. /// option should be added to the message (if true).
  78. /// @param srvid server id to be stored in the message.
  79. ///
  80. /// @return An object representing the created message.
  81. Pkt6Ptr generateMessage(uint8_t msg_type,
  82. const uint8_t fqdn_flags,
  83. const std::string& fqdn_domain_name,
  84. const Option6ClientFqdn::DomainNameType
  85. fqdn_type,
  86. const bool include_oro,
  87. OptionPtr srvid = OptionPtr()) {
  88. Pkt6Ptr pkt = Pkt6Ptr(new Pkt6(msg_type, 1234));
  89. pkt->setRemoteAddr(IOAddress("fe80::abcd"));
  90. Option6IAPtr ia = generateIA(D6O_IA_NA, 234, 1500, 3000);
  91. if (msg_type != DHCPV6_REPLY) {
  92. IOAddress hint("2001:db8:1:1::dead:beef");
  93. OptionPtr hint_opt(new Option6IAAddr(D6O_IAADDR, hint, 300, 500));
  94. ia->addOption(hint_opt);
  95. pkt->addOption(ia);
  96. }
  97. OptionPtr clientid = generateClientId();
  98. pkt->addOption(clientid);
  99. if (srvid && (msg_type != DHCPV6_SOLICIT)) {
  100. pkt->addOption(srvid);
  101. }
  102. pkt->addOption(createClientFqdn(fqdn_flags, fqdn_domain_name,
  103. fqdn_type));
  104. if (include_oro) {
  105. OptionUint16ArrayPtr oro(new OptionUint16Array(Option::V6,
  106. D6O_ORO));
  107. oro->addValue(D6O_CLIENT_FQDN);
  108. pkt->addOption(oro);
  109. }
  110. return (pkt);
  111. }
  112. /// @brief Creates instance of the DHCPv6 message with client id and
  113. /// server id.
  114. ///
  115. /// @param msg_type A type of the message to be created.
  116. /// @param srv An object representing the DHCPv6 server, which
  117. /// is used to generate the client identifier.
  118. ///
  119. /// @return An object representing the created message.
  120. Pkt6Ptr generateMessageWithIds(const uint8_t msg_type,
  121. NakedDhcpv6Srv& srv) {
  122. Pkt6Ptr pkt = Pkt6Ptr(new Pkt6(msg_type, 1234));
  123. // Generate client-id.
  124. OptionPtr opt_clientid = generateClientId();
  125. pkt->addOption(opt_clientid);
  126. if (msg_type != DHCPV6_SOLICIT) {
  127. // Generate server-id.
  128. pkt->addOption(srv.getServerID());
  129. }
  130. return (pkt);
  131. }
  132. /// @brief Returns an instance of the option carrying FQDN.
  133. ///
  134. /// @param pkt A message holding FQDN option to be returned.
  135. ///
  136. /// @return An object representing DHCPv6 Client FQDN option.
  137. Option6ClientFqdnPtr getClientFqdnOption(const Pkt6Ptr& pkt) {
  138. return (boost::dynamic_pointer_cast<Option6ClientFqdn>
  139. (pkt->getOption(D6O_CLIENT_FQDN)));
  140. }
  141. /// @brief Adds IA option to the message.
  142. ///
  143. /// Addded option holds an address.
  144. ///
  145. /// @param iaid IAID
  146. /// @param pkt A DHCPv6 message to which the IA option should be added.
  147. void addIA(const uint32_t iaid, const IOAddress& addr, Pkt6Ptr& pkt) {
  148. Option6IAPtr opt_ia = generateIA(D6O_IA_NA, iaid, 1500, 3000);
  149. Option6IAAddrPtr opt_iaaddr(new Option6IAAddr(D6O_IAADDR, addr,
  150. 300, 500));
  151. opt_ia->addOption(opt_iaaddr);
  152. pkt->addOption(opt_ia);
  153. }
  154. /// @brief Adds IA option to the message.
  155. ///
  156. /// Added option holds status code.
  157. ///
  158. /// @param iaid IAID
  159. /// @param status_code Status code
  160. /// @param pkt A DHCPv6 message to which the option should be added.
  161. void addIA(const uint32_t iaid, const uint16_t status_code, Pkt6Ptr& pkt) {
  162. Option6IAPtr opt_ia = generateIA(D6O_IA_NA, iaid, 1500, 3000);
  163. addStatusCode(status_code, "", opt_ia);
  164. pkt->addOption(opt_ia);
  165. }
  166. /// @brief Creates status code with the specified code and message.
  167. ///
  168. /// @param code A status code.
  169. /// @param msg A string representation of the message to be added to the
  170. /// Status Code option.
  171. ///
  172. /// @return An object representing the Status Code option.
  173. OptionCustomPtr createStatusCode(const uint16_t code,
  174. const std::string& msg) {
  175. OptionDefinition def("status-code", D6O_STATUS_CODE, "record");
  176. def.addRecordField("uint16");
  177. def.addRecordField("string");
  178. OptionCustomPtr opt_status(new OptionCustom(def, Option::V6));
  179. opt_status->writeInteger(code);
  180. if (!msg.empty()) {
  181. opt_status->writeString(msg, 1);
  182. }
  183. return (opt_status);
  184. }
  185. /// @brief Adds Status Code option to the IA.
  186. ///
  187. /// @param code A status code value.
  188. /// @param msg A string representation of the message to be added to the
  189. /// Status Code option.
  190. void addStatusCode(const uint16_t code, const std::string& msg,
  191. Option6IAPtr& opt_ia) {
  192. opt_ia->addOption(createStatusCode(code, msg));
  193. }
  194. /// @brief Verifies if the DHCPv6 server processes DHCPv6 Client FQDN option
  195. /// as expected.
  196. ///
  197. /// This function simulates generation of the client's message holding FQDN.
  198. /// It then calls the server's @c Dhcpv6Srv::processClientFqdn option to
  199. /// generate server's response to the FQDN. This function returns the FQDN
  200. /// which should be appended to the server's response to the client.
  201. /// This function verifies that the FQDN option returned is correct.
  202. ///
  203. /// @param msg_type A type of the client's message.
  204. /// @param in_flags A value of flags field to be set for the FQDN carried
  205. /// in the client's message.
  206. /// @param in_domain_name A domain name to be carried in the client's FQDN
  207. /// option.
  208. /// @param in_domain_type A type of the domain name to be carried in the
  209. /// client's FQDM option (partial or fully qualified).
  210. /// @param exp_flags A value of flags expected in the FQDN sent by a server.
  211. /// @param exp_domain_name A domain name expected in the FQDN sent by a
  212. /// server.
  213. void testFqdn(const uint16_t msg_type,
  214. const uint8_t in_flags,
  215. const std::string& in_domain_name,
  216. const Option6ClientFqdn::DomainNameType in_domain_type,
  217. const uint8_t exp_flags,
  218. const std::string& exp_domain_name) {
  219. NakedDhcpv6Srv srv(0);
  220. Pkt6Ptr question = generateMessage(msg_type,
  221. in_flags,
  222. in_domain_name,
  223. in_domain_type,
  224. true);
  225. ASSERT_TRUE(getClientFqdnOption(question));
  226. Pkt6Ptr answer(new Pkt6(msg_type == DHCPV6_SOLICIT ? DHCPV6_ADVERTISE :
  227. DHCPV6_REPLY, question->getTransid()));
  228. ASSERT_NO_THROW(srv.processClientFqdn(question, answer));
  229. Option6ClientFqdnPtr answ_fqdn = boost::dynamic_pointer_cast<
  230. Option6ClientFqdn>(answer->getOption(D6O_CLIENT_FQDN));
  231. ASSERT_TRUE(answ_fqdn);
  232. const bool flag_n = (exp_flags & Option6ClientFqdn::FLAG_N) != 0;
  233. const bool flag_s = (exp_flags & Option6ClientFqdn::FLAG_S) != 0;
  234. const bool flag_o = (exp_flags & Option6ClientFqdn::FLAG_O) != 0;
  235. EXPECT_EQ(flag_n, answ_fqdn->getFlag(Option6ClientFqdn::FLAG_N));
  236. EXPECT_EQ(flag_s, answ_fqdn->getFlag(Option6ClientFqdn::FLAG_S));
  237. EXPECT_EQ(flag_o, answ_fqdn->getFlag(Option6ClientFqdn::FLAG_O));
  238. EXPECT_EQ(exp_domain_name, answ_fqdn->getDomainName());
  239. EXPECT_EQ(Option6ClientFqdn::FULL, answ_fqdn->getDomainNameType());
  240. }
  241. /// @brief Tests that the client's message holding an FQDN is processed
  242. /// and that lease is acquired.
  243. ///
  244. /// @param msg_type A type of the client's message.
  245. /// @param hostname A domain name in the client's FQDN.
  246. /// @param srv A server object, used to process the message.
  247. /// @param include_oro A boolean value which indicates whether the ORO
  248. /// option should be included in the client's message (if true) or not
  249. /// (if false). In the former case, the function will expect that server
  250. /// responds with the FQDN option. In the latter case, the function expects
  251. /// that the server doesn't respond with the FQDN.
  252. void testProcessMessage(const uint8_t msg_type,
  253. const std::string& hostname,
  254. NakedDhcpv6Srv& srv,
  255. const bool include_oro = true) {
  256. // Create a message of a specified type, add server id and
  257. // FQDN option.
  258. OptionPtr srvid = srv.getServerID();
  259. Pkt6Ptr req = generateMessage(msg_type, Option6ClientFqdn::FLAG_S,
  260. hostname,
  261. Option6ClientFqdn::FULL,
  262. include_oro, srvid);
  263. // For different client's message types we have to invoke different
  264. // functions to generate response.
  265. Pkt6Ptr reply;
  266. if (msg_type == DHCPV6_SOLICIT) {
  267. ASSERT_NO_THROW(reply = srv.processSolicit(req));
  268. } else if (msg_type == DHCPV6_REQUEST) {
  269. ASSERT_NO_THROW(reply = srv.processRequest(req));
  270. } else if (msg_type == DHCPV6_RENEW) {
  271. ASSERT_NO_THROW(reply = srv.processRequest(req));
  272. } else if (msg_type == DHCPV6_RELEASE) {
  273. // For Release no lease will be acquired so we have to leave
  274. // function here.
  275. ASSERT_NO_THROW(reply = srv.processRelease(req));
  276. return;
  277. } else {
  278. // We are not interested in testing other message types.
  279. return;
  280. }
  281. // For Solicit, we will get different message type obviously.
  282. if (msg_type == DHCPV6_SOLICIT) {
  283. checkResponse(reply, DHCPV6_ADVERTISE, 1234);
  284. } else {
  285. checkResponse(reply, DHCPV6_REPLY, 1234);
  286. }
  287. // Check verify that IA_NA is correct.
  288. Option6IAAddrPtr addr =
  289. checkIA_NA(reply, 234, subnet_->getT1(), subnet_->getT2());
  290. ASSERT_TRUE(addr);
  291. // Check that we have got the address we requested.
  292. checkIAAddr(addr, IOAddress("2001:db8:1:1::dead:beef"),
  293. Lease::TYPE_NA);
  294. if (msg_type != DHCPV6_SOLICIT) {
  295. // Check that the lease exists.
  296. Lease6Ptr lease =
  297. checkLease(duid_, reply->getOption(D6O_IA_NA), addr);
  298. ASSERT_TRUE(lease);
  299. }
  300. // The Client FQDN option should be always present in the server's
  301. // response, regardless if requested using ORO or not.
  302. ASSERT_TRUE(reply->getOption(D6O_CLIENT_FQDN));
  303. }
  304. /// @brief Verify that NameChangeRequest holds valid values.
  305. ///
  306. /// This function picks first NameChangeRequest from the internal server's
  307. /// queue and checks that it holds valid parameters. The NameChangeRequest
  308. /// is removed from the queue.
  309. ///
  310. /// @param srv A server object holding a queue of NameChangeRequests.
  311. /// @param type An expected type of the NameChangeRequest (Add or Remove).
  312. /// @param reverse An expected setting of the reverse update flag.
  313. /// @param forward An expected setting of the forward udpate flag.
  314. /// @param addr A string representation of the IPv6 address held in the
  315. /// NameChangeRequest.
  316. /// @param dhcid An expected DHCID value.
  317. /// @param expires A timestamp when the lease associated with the
  318. /// NameChangeRequest expires.
  319. /// @param len A valid lifetime of the lease associated with the
  320. /// NameChangeRequest.
  321. void verifyNameChangeRequest(NakedDhcpv6Srv& srv,
  322. const isc::dhcp_ddns::NameChangeType type,
  323. const bool reverse, const bool forward,
  324. const std::string& addr,
  325. const std::string& dhcid,
  326. const uint16_t expires,
  327. const uint16_t len) {
  328. NameChangeRequest ncr = srv.name_change_reqs_.front();
  329. EXPECT_EQ(type, ncr.getChangeType());
  330. EXPECT_EQ(forward, ncr.isForwardChange());
  331. EXPECT_EQ(reverse, ncr.isReverseChange());
  332. EXPECT_EQ(addr, ncr.getIpAddress());
  333. EXPECT_EQ(dhcid, ncr.getDhcid().toStr());
  334. EXPECT_EQ(expires, ncr.getLeaseExpiresOn());
  335. EXPECT_EQ(len, ncr.getLeaseLength());
  336. EXPECT_EQ(isc::dhcp_ddns::ST_NEW, ncr.getStatus());
  337. srv.name_change_reqs_.pop();
  338. }
  339. // Holds a lease used by a test.
  340. Lease6Ptr lease_;
  341. };
  342. // A set of tests verifying server's behaviour when it receives the DHCPv6
  343. // Client Fqdn Option.
  344. // @todo: Extend these tests once appropriate configuration parameters are
  345. // implemented (ticket #3034).
  346. // Test server's response when client requests that server performs AAAA update.
  347. TEST_F(FqdnDhcpv6SrvTest, serverAAAAUpdate) {
  348. testFqdn(DHCPV6_SOLICIT, Option6ClientFqdn::FLAG_S,
  349. "myhost.example.com",
  350. Option6ClientFqdn::FULL, Option6ClientFqdn::FLAG_S,
  351. "myhost.example.com.");
  352. }
  353. // Test server's response when client provides partial domain-name and requests
  354. // that server performs AAAA update.
  355. TEST_F(FqdnDhcpv6SrvTest, serverAAAAUpdatePartialName) {
  356. testFqdn(DHCPV6_SOLICIT, Option6ClientFqdn::FLAG_S, "myhost",
  357. Option6ClientFqdn::PARTIAL, Option6ClientFqdn::FLAG_S,
  358. "myhost.example.com.");
  359. }
  360. // Test server's response when client provides empty domain-name and requests
  361. // that server performs AAAA update.
  362. TEST_F(FqdnDhcpv6SrvTest, serverAAAAUpdateNoName) {
  363. testFqdn(DHCPV6_SOLICIT, Option6ClientFqdn::FLAG_S, "",
  364. Option6ClientFqdn::PARTIAL, Option6ClientFqdn::FLAG_S,
  365. "myhost.example.com.");
  366. }
  367. // Test server's response when client requests no DNS update.
  368. TEST_F(FqdnDhcpv6SrvTest, noUpdate) {
  369. testFqdn(DHCPV6_SOLICIT, Option6ClientFqdn::FLAG_N,
  370. "myhost.example.com",
  371. Option6ClientFqdn::FULL, Option6ClientFqdn::FLAG_N,
  372. "myhost.example.com.");
  373. }
  374. // Test server's response when client requests that server delegates the AAAA
  375. // update to the client and this delegation is not allowed.
  376. TEST_F(FqdnDhcpv6SrvTest, clientAAAAUpdateNotAllowed) {
  377. testFqdn(DHCPV6_SOLICIT, 0, "myhost.example.com.",
  378. Option6ClientFqdn::FULL,
  379. Option6ClientFqdn::FLAG_S | Option6ClientFqdn::FLAG_O,
  380. "myhost.example.com.");
  381. }
  382. // Test that exception is thrown if supplied NULL answer packet when
  383. // creating NameChangeRequests.
  384. TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequestsNoAnswer) {
  385. NakedDhcpv6Srv srv(0);
  386. Pkt6Ptr answer;
  387. EXPECT_THROW(srv.createNameChangeRequests(answer),
  388. isc::Unexpected);
  389. }
  390. // Test that exception is thrown if supplied answer from the server
  391. // contains no DUID when creating NameChangeRequests.
  392. TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequestsNoDUID) {
  393. NakedDhcpv6Srv srv(0);
  394. Pkt6Ptr answer = Pkt6Ptr(new Pkt6(DHCPV6_REPLY, 1234));
  395. Option6ClientFqdnPtr fqdn = createClientFqdn(Option6ClientFqdn::FLAG_S,
  396. "myhost.example.com",
  397. Option6ClientFqdn::FULL);
  398. answer->addOption(fqdn);
  399. EXPECT_THROW(srv.createNameChangeRequests(answer), isc::Unexpected);
  400. }
  401. // Test no NameChangeRequests if Client FQDN is not added to the server's
  402. // response.
  403. TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequestsNoFQDN) {
  404. NakedDhcpv6Srv srv(0);
  405. // Create Reply message with Client Id and Server id.
  406. Pkt6Ptr answer = generateMessageWithIds(DHCPV6_REPLY, srv);
  407. ASSERT_NO_THROW(srv.createNameChangeRequests(answer));
  408. // There should be no new NameChangeRequests.
  409. EXPECT_TRUE(srv.name_change_reqs_.empty());
  410. }
  411. // Test that NameChangeRequests are not generated if an answer message
  412. // contains no addresses.
  413. TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequestsNoAddr) {
  414. NakedDhcpv6Srv srv(0);
  415. // Create Reply message with Client Id and Server id.
  416. Pkt6Ptr answer = generateMessageWithIds(DHCPV6_REPLY, srv);
  417. // Add Client FQDN option.
  418. Option6ClientFqdnPtr fqdn = createClientFqdn(Option6ClientFqdn::FLAG_S,
  419. "myhost.example.com",
  420. Option6ClientFqdn::FULL);
  421. answer->addOption(fqdn);
  422. ASSERT_NO_THROW(srv.createNameChangeRequests(answer));
  423. // We didn't add any IAs, so there should be no NameChangeRequests in th
  424. // queue.
  425. ASSERT_TRUE(srv.name_change_reqs_.empty());
  426. }
  427. // Test that exactly one NameChangeRequest is created as a result of processing
  428. // the answer message which holds 3 IAs and when FQDN is specified.
  429. TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequests) {
  430. NakedDhcpv6Srv srv(0);
  431. // Create Reply message with Client Id and Server id.
  432. Pkt6Ptr answer = generateMessageWithIds(DHCPV6_REPLY, srv);
  433. // Create three IAs, each having different address.
  434. addIA(1234, IOAddress("2001:db8:1::1"), answer);
  435. addIA(2345, IOAddress("2001:db8:1::2"), answer);
  436. addIA(3456, IOAddress("2001:db8:1::3"), answer);
  437. // Use domain name in upper case. It should be converted to lower-case
  438. // before DHCID is calculated. So, we should get the same result as if
  439. // we typed domain name in lower-case.
  440. Option6ClientFqdnPtr fqdn = createClientFqdn(Option6ClientFqdn::FLAG_S,
  441. "MYHOST.EXAMPLE.COM",
  442. Option6ClientFqdn::FULL);
  443. answer->addOption(fqdn);
  444. // Create NameChangeRequest for the first allocated address.
  445. ASSERT_NO_THROW(srv.createNameChangeRequests(answer));
  446. ASSERT_EQ(1, srv.name_change_reqs_.size());
  447. // Verify that NameChangeRequest is correct.
  448. verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
  449. "2001:db8:1::1",
  450. "000201415AA33D1187D148275136FA30300478"
  451. "FAAAA3EBD29826B5C907B2C9268A6F52",
  452. 0, 500);
  453. }
  454. // Test creation of the NameChangeRequest to remove both forward and reverse
  455. // mapping for the given lease.
  456. TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestFwdRev) {
  457. NakedDhcpv6Srv srv(0);
  458. lease_->fqdn_fwd_ = true;
  459. lease_->fqdn_rev_ = true;
  460. // Part of the domain name is in upper case, to test that it gets converted
  461. // to lower case before DHCID is computed. So, we should get the same DHCID
  462. // as if we typed domain-name in lower case.
  463. lease_->hostname_ = "MYHOST.example.com.";
  464. ASSERT_NO_THROW(srv.createRemovalNameChangeRequest(lease_));
  465. ASSERT_EQ(1, srv.name_change_reqs_.size());
  466. verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_REMOVE, true, true,
  467. "2001:db8:1::1",
  468. "000201415AA33D1187D148275136FA30300478"
  469. "FAAAA3EBD29826B5C907B2C9268A6F52",
  470. 0, 502);
  471. }
  472. // Test creation of the NameChangeRequest to remove reverse mapping for the
  473. // given lease.
  474. TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestRev) {
  475. NakedDhcpv6Srv srv(0);
  476. lease_->fqdn_fwd_ = false;
  477. lease_->fqdn_rev_ = true;
  478. lease_->hostname_ = "myhost.example.com.";
  479. ASSERT_NO_THROW(srv.createRemovalNameChangeRequest(lease_));
  480. ASSERT_EQ(1, srv.name_change_reqs_.size());
  481. verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_REMOVE, true, false,
  482. "2001:db8:1::1",
  483. "000201415AA33D1187D148275136FA30300478"
  484. "FAAAA3EBD29826B5C907B2C9268A6F52",
  485. 0, 502);
  486. }
  487. // Test that NameChangeRequest to remove DNS records is not generated when
  488. // neither forward nor reverse DNS update has been performed for a lease.
  489. TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestNoUpdate) {
  490. NakedDhcpv6Srv srv(0);
  491. lease_->fqdn_fwd_ = false;
  492. lease_->fqdn_rev_ = false;
  493. ASSERT_NO_THROW(srv.createRemovalNameChangeRequest(lease_));
  494. EXPECT_TRUE(srv.name_change_reqs_.empty());
  495. }
  496. // Test that NameChangeRequest is not generated if the hostname hasn't been
  497. // specified for a lease for which forward and reverse mapping has been set.
  498. TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestNoHostname) {
  499. NakedDhcpv6Srv srv(0);
  500. lease_->fqdn_fwd_ = true;
  501. lease_->fqdn_rev_ = true;
  502. lease_->hostname_ = "";
  503. ASSERT_NO_THROW(srv.createRemovalNameChangeRequest(lease_));
  504. EXPECT_TRUE(srv.name_change_reqs_.empty());
  505. }
  506. // Test that NameChangeRequest is not generated if the invalid hostname has
  507. // been specified for a lease for which forward and reverse mapping has been
  508. // set.
  509. TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestWrongHostname) {
  510. NakedDhcpv6Srv srv(0);
  511. lease_->fqdn_fwd_ = true;
  512. lease_->fqdn_rev_ = true;
  513. lease_->hostname_ = "myhost..example.com.";
  514. ASSERT_NO_THROW(srv.createRemovalNameChangeRequest(lease_));
  515. EXPECT_TRUE(srv.name_change_reqs_.empty());
  516. }
  517. // Test that Advertise message generated in a response to the Solicit will
  518. // not result in generation if the NameChangeRequests.
  519. TEST_F(FqdnDhcpv6SrvTest, processSolicit) {
  520. NakedDhcpv6Srv srv(0);
  521. // Create a Solicit message with FQDN option and generate server's
  522. // response using processSolicit function.
  523. testProcessMessage(DHCPV6_SOLICIT, "myhost.example.com", srv);
  524. EXPECT_TRUE(srv.name_change_reqs_.empty());
  525. }
  526. // Test that client may send two requests, each carrying FQDN option with
  527. // a different domain-name. Server should use existing lease for the second
  528. // request but modify the DNS entries for the lease according to the contents
  529. // of the FQDN sent in the second request.
  530. TEST_F(FqdnDhcpv6SrvTest, processTwoRequests) {
  531. NakedDhcpv6Srv srv(0);
  532. // Create a Request message with FQDN option and generate server's
  533. // response using processRequest function. This will result in the
  534. // creation of a new lease and the appropriate NameChangeRequest
  535. // to add both reverse and forward mapping to DNS.
  536. testProcessMessage(DHCPV6_REQUEST, "myhost.example.com", srv);
  537. ASSERT_EQ(1, srv.name_change_reqs_.size());
  538. verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
  539. "2001:db8:1:1::dead:beef",
  540. "000201415AA33D1187D148275136FA30300478"
  541. "FAAAA3EBD29826B5C907B2C9268A6F52",
  542. 0, 4000);
  543. // Client may send another request message with a new domain-name. In this
  544. // case the same lease will be returned. The existing DNS entry needs to
  545. // be replaced with a new one. Server should determine that the different
  546. // FQDN has been already added to the DNS. As a result, the old DNS
  547. // entries should be removed and the entries for the new domain-name
  548. // should be added. Therefore, we expect two NameChangeRequests. One to
  549. // remove the existing entries, one to add new entries.
  550. testProcessMessage(DHCPV6_REQUEST, "otherhost.example.com", srv);
  551. ASSERT_EQ(2, srv.name_change_reqs_.size());
  552. verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_REMOVE, true, true,
  553. "2001:db8:1:1::dead:beef",
  554. "000201415AA33D1187D148275136FA30300478"
  555. "FAAAA3EBD29826B5C907B2C9268A6F52",
  556. 0, 4000);
  557. verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
  558. "2001:db8:1:1::dead:beef",
  559. "000201D422AA463306223D269B6CB7AFE7AAD265FC"
  560. "EA97F93623019B2E0D14E5323D5A",
  561. 0, 4000);
  562. }
  563. // Test that NameChangeRequest is not generated when Solicit message is sent.
  564. // The Solicit is here sent after a lease has been allocated for a client.
  565. // The Solicit conveys a different hostname which would trigger updates to
  566. // DNS if the Request was sent instead of Soicit. The code should differentiate
  567. // behavior depending whether Solicit or Request is sent.
  568. TEST_F(FqdnDhcpv6SrvTest, processRequestSolicit) {
  569. NakedDhcpv6Srv srv(0);
  570. // Create a Request message with FQDN option and generate server's
  571. // response using processRequest function. This will result in the
  572. // creation of a new lease and the appropriate NameChangeRequest
  573. // to add both reverse and forward mapping to DNS.
  574. testProcessMessage(DHCPV6_REQUEST, "myhost.example.com", srv);
  575. ASSERT_EQ(1, srv.name_change_reqs_.size());
  576. verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
  577. "2001:db8:1:1::dead:beef",
  578. "000201415AA33D1187D148275136FA30300478"
  579. "FAAAA3EBD29826B5C907B2C9268A6F52",
  580. 0, 4000);
  581. // When the returning client sends Solicit the code should never generate
  582. // NameChangeRequest and preserve existing DNS entries for the client.
  583. // The NameChangeRequest should only be generated when a client sends
  584. // Request or Renew.
  585. testProcessMessage(DHCPV6_SOLICIT, "otherhost.example.com", srv);
  586. ASSERT_TRUE(srv.name_change_reqs_.empty());
  587. }
  588. // Test that client may send Request followed by the Renew, both holding
  589. // FQDN options, but each option holding different domain-name. The Renew
  590. // should result in generation of the two NameChangeRequests, one to remove
  591. // DNS entry added previously when Request was processed, another one to
  592. // add a new entry for the FQDN held in the Renew.
  593. TEST_F(FqdnDhcpv6SrvTest, processRequestRenew) {
  594. NakedDhcpv6Srv srv(0);
  595. // Create a Request message with FQDN option and generate server's
  596. // response using processRequest function. This will result in the
  597. // creation of a new lease and the appropriate NameChangeRequest
  598. // to add both reverse and forward mapping to DNS.
  599. testProcessMessage(DHCPV6_REQUEST, "myhost.example.com", srv);
  600. ASSERT_EQ(1, srv.name_change_reqs_.size());
  601. verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
  602. "2001:db8:1:1::dead:beef",
  603. "000201415AA33D1187D148275136FA30300478"
  604. "FAAAA3EBD29826B5C907B2C9268A6F52",
  605. 0, 4000);
  606. // Client may send Renew message with a new domain-name. In this
  607. // case the same lease will be returned. The existing DNS entry needs to
  608. // be replaced with a new one. Server should determine that the different
  609. // FQDN has been already added to the DNS. As a result, the old DNS
  610. // entries should be removed and the entries for the new domain-name
  611. // should be added. Therefore, we expect two NameChangeRequests. One to
  612. // remove the existing entries, one to add new entries.
  613. testProcessMessage(DHCPV6_RENEW, "otherhost.example.com", srv);
  614. ASSERT_EQ(2, srv.name_change_reqs_.size());
  615. verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_REMOVE, true, true,
  616. "2001:db8:1:1::dead:beef",
  617. "000201415AA33D1187D148275136FA30300478"
  618. "FAAAA3EBD29826B5C907B2C9268A6F52",
  619. 0, 4000);
  620. verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
  621. "2001:db8:1:1::dead:beef",
  622. "000201D422AA463306223D269B6CB7AFE7AAD265FC"
  623. "EA97F93623019B2E0D14E5323D5A",
  624. 0, 4000);
  625. }
  626. TEST_F(FqdnDhcpv6SrvTest, processRequestRelease) {
  627. NakedDhcpv6Srv srv(0);
  628. // Create a Request message with FQDN option and generate server's
  629. // response using processRequest function. This will result in the
  630. // creation of a new lease and the appropriate NameChangeRequest
  631. // to add both reverse and forward mapping to DNS.
  632. testProcessMessage(DHCPV6_REQUEST, "myhost.example.com", srv);
  633. ASSERT_EQ(1, srv.name_change_reqs_.size());
  634. verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
  635. "2001:db8:1:1::dead:beef",
  636. "000201415AA33D1187D148275136FA30300478"
  637. "FAAAA3EBD29826B5C907B2C9268A6F52",
  638. 0, 4000);
  639. // Client may send Release message. In this case the lease should be
  640. // removed and all existing DNS entries for this lease should be
  641. // also removed. Therefore, we expect that single NameChangeRequest to
  642. // remove DNS entries is generated.
  643. testProcessMessage(DHCPV6_RELEASE, "otherhost.example.com", srv);
  644. ASSERT_EQ(1, srv.name_change_reqs_.size());
  645. verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_REMOVE, true, true,
  646. "2001:db8:1:1::dead:beef",
  647. "000201415AA33D1187D148275136FA30300478"
  648. "FAAAA3EBD29826B5C907B2C9268A6F52",
  649. 0, 4000);
  650. }
  651. // Checks that the server include DHCPv6 Client FQDN option in its
  652. // response even when client doesn't request this option using ORO.
  653. TEST_F(FqdnDhcpv6SrvTest, processRequestWithoutFqdn) {
  654. NakedDhcpv6Srv srv(0);
  655. // The last parameter disables use of the ORO to request FQDN option
  656. // In this case, we expect that the FQDN option will not be included
  657. // in the server's response. The testProcessMessage will check that.
  658. testProcessMessage(DHCPV6_REQUEST, "myhost.example.com", srv, false);
  659. ASSERT_EQ(1, srv.name_change_reqs_.size());
  660. verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
  661. "2001:db8:1:1::dead:beef",
  662. "000201415AA33D1187D148275136FA30300478"
  663. "FAAAA3EBD29826B5C907B2C9268A6F52",
  664. 0, 4000);
  665. }
  666. // Checks that when the server reuses expired lease, the NameChangeRequest
  667. // is generated to remove the DNS mapping for the expired lease and second
  668. // NameChangeRequest to add a DNS mapping for a new lease.
  669. TEST_F(FqdnDhcpv6SrvTest, processRequestReuseExpiredLease) {
  670. // This address will be used throughout the test.
  671. IOAddress addr("2001:db8:1:1::dead:beef");
  672. // We are going to configure a subnet with a pool that consists of
  673. // exactly one address. This address will be handed out to the
  674. // client, will get expired and then be reused.
  675. CfgMgr::instance().deleteSubnets6();
  676. subnet_ = Subnet6Ptr(new Subnet6(IOAddress("2001:db8:1:1::"), 56, 1, 2,
  677. 3, 4));
  678. pool_ = Pool6Ptr(new Pool6(Lease::TYPE_NA, addr, addr));
  679. subnet_->addPool(pool_);
  680. CfgMgr::instance().addSubnet6(subnet_);
  681. // Allocate a lease.
  682. NakedDhcpv6Srv srv(0);
  683. testProcessMessage(DHCPV6_REQUEST, "myhost.example.com", srv);
  684. // Test that the appropriate NameChangeRequest has been generated.
  685. ASSERT_EQ(1, srv.name_change_reqs_.size());
  686. verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
  687. "2001:db8:1:1::dead:beef",
  688. "000201415AA33D1187D148275136FA30300478"
  689. "FAAAA3EBD29826B5C907B2C9268A6F52",
  690. 0, 4);
  691. // Get the lease acquired and modify it. In particular, expire it.
  692. Lease6Ptr lease =
  693. LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA, addr);
  694. ASSERT_TRUE(lease);
  695. // One of the following: IAID, DUID or subnet identifier has to be changed
  696. // because otherwise the allocation engine will treat the lease as
  697. // being renewed by the same client. If we at least change subnet identifier
  698. // the lease will be treated as expired lease to be reused.
  699. ++lease->subnet_id_;
  700. // Move the cllt back in time and make sure that the lease got expired.
  701. lease->cltt_ = time(NULL) - 10;
  702. lease->valid_lft_ = 5;
  703. ASSERT_TRUE(lease->expired());
  704. // Change the hostname so as the name change request for removing existing
  705. // DNS mapping is generated.
  706. lease->hostname_ = "otherhost.example.com.";
  707. // Update the lease in the lease database.
  708. LeaseMgrFactory::instance().updateLease6(lease);
  709. // Simulate another lease acquisition. Since, our pool consists of
  710. // exactly one address and this address is used by the lease in the
  711. // lease database, it is guaranteed that the allocation engine will
  712. // reuse this lease.
  713. testProcessMessage(DHCPV6_REQUEST, "myhost.example.com.", srv);
  714. ASSERT_EQ(2, srv.name_change_reqs_.size());
  715. // The first name change request generated, should remove a DNS
  716. // mapping for an expired lease.
  717. verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_REMOVE, true, true,
  718. "2001:db8:1:1::dead:beef",
  719. "000201D422AA463306223D269B6CB7AFE7AAD2"
  720. "65FCEA97F93623019B2E0D14E5323D5A",
  721. 0, 5);
  722. // The second name change request should add a DNS mapping for
  723. // a new lease.
  724. verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
  725. "2001:db8:1:1::dead:beef",
  726. "000201415AA33D1187D148275136FA30300478"
  727. "FAAAA3EBD29826B5C907B2C9268A6F52",
  728. 0, 4);
  729. }
  730. } // end of anonymous namespace